diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
index 64c484b1d0853..a4ddf64e88ce9 100644
--- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
+++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems
@@ -30,6 +30,7 @@
+
diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
index 466c18e00e098..782235465b2c1 100644
--- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
+++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx
@@ -343,4 +343,10 @@
Use parameter null checking
+
+ Lambda expression can be removed
+
+
+ Remove unnecessary lambda expression
+
\ No newline at end of file
diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs
new file mode 100644
index 0000000000000..25cf721c05f31
--- /dev/null
+++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs
@@ -0,0 +1,317 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+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.Shared.Extensions;
+using Microsoft.CodeAnalysis.Text;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression
+{
+ ///
+ /// DiagnosticAnalyzer that looks code like Goo(() => Bar()) and offers to convert it to Goo(Bar).
+ /// This is only offered on C# 11 and above where this delegate can be cached and will not cause allocations each
+ /// time.
+ ///
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ internal sealed class CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
+ {
+ public CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer()
+ : base(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId,
+ EnforceOnBuildValues.RemoveUnnecessaryLambdaExpression,
+ CSharpCodeStyleOptions.PreferMethodGroupConversion,
+ LanguageNames.CSharp,
+ new LocalizableResourceString(nameof(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)),
+ new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_removed), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)),
+ isUnnecessary: true)
+ {
+ }
+
+ public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
+ => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
+
+ protected override void InitializeWorker(AnalysisContext context)
+ {
+ context.RegisterCompilationStartAction(context =>
+ {
+ if (context.Compilation.LanguageVersion().IsCSharp11OrAbove())
+ {
+ var expressionType = context.Compilation.ExpressionOfTType();
+ context.RegisterSyntaxNodeAction(
+ c => AnalyzeSyntax(c, expressionType),
+ SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.AnonymousMethodExpression);
+ }
+ });
+ }
+
+ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol? expressionType)
+ {
+ var cancellationToken = context.CancellationToken;
+ var semanticModel = context.SemanticModel;
+ var syntaxTree = semanticModel.SyntaxTree;
+
+ var preference = context.GetOption(CSharpCodeStyleOptions.PreferMethodGroupConversion);
+ if (preference.Notification.Severity == ReportDiagnostic.Suppress)
+ {
+ // User doesn't care about this rule.
+ return;
+ }
+
+ var anonymousFunction = (AnonymousFunctionExpressionSyntax)context.Node;
+
+ // Syntax checks first.
+
+ // Don't simplify static lambdas. The user made them explicitly static to make it clear it must only cause
+ // a single allocation for the cached delegate. If we get rid of the lambda (and thus the static-keyword) it
+ // won't be clear anymore if the member-group-conversion allocation is cached or not.
+ if (anonymousFunction.Modifiers.Any(SyntaxKind.StaticKeyword))
+ return;
+
+ if (!TryGetAnonymousFunctionInvocation(anonymousFunction, out var invocation, out var wasAwaited))
+ return;
+
+ // If we had an async function, but we didn't await the expression inside then we can't convert this. The
+ // underlying value was wrapped into a task, and that won't work if directly referencing the function.
+ if (wasAwaited != anonymousFunction.Modifiers.Any(SyntaxKind.AsyncKeyword))
+ return;
+
+ // We have to have an invocation in the lambda like `() => X()` or `() => expr.X()`.
+ var invokedExpression = invocation.Expression;
+ if (invokedExpression is not SimpleNameSyntax and not MemberAccessExpressionSyntax)
+ return;
+
+ // lambda and invocation have to agree on number of parameters.
+ var parameters = GetParameters(anonymousFunction);
+ if (parameters.Count != invocation.ArgumentList.Arguments.Count)
+ return;
+
+ // parameters must be passed 1:1 from lambda to invocation.
+ for (int i = 0, n = parameters.Count; i < n; i++)
+ {
+ var parameter = parameters[i];
+ var argument = invocation.ArgumentList.Arguments[i];
+
+ if (argument.Expression is not IdentifierNameSyntax argumentIdentifier)
+ return;
+
+ if (parameter.Identifier.ValueText != argumentIdentifier.Identifier.ValueText)
+ return;
+ }
+
+ // if we have `() => new C().X()` then converting to `new C().X` very much changes the meaning.
+ if (MayHaveSideEffects(invokedExpression))
+ return;
+
+ // Looks like a reasonable candidate to simplify. Now switch to semantics to check for sure.
+
+ if (CSharpSemanticFacts.Instance.IsInExpressionTree(semanticModel, anonymousFunction, expressionType, cancellationToken))
+ return;
+
+ // If we have `object obj = x => Goo(x);` we don't want to simplify. The compiler warns if you write
+ // `object obj = Goo;` because of the conversion to a non-delegate type. While we could insert a cast here
+ // to make this work, that goes against the spirit of this analyzer/fixer just removing code.
+ var lambdaTypeInfo = semanticModel.GetTypeInfo(anonymousFunction, cancellationToken);
+ if (lambdaTypeInfo.ConvertedType == null || lambdaTypeInfo.ConvertedType.SpecialType is SpecialType.System_Object)
+ return;
+
+ var lambdaSymbolInfo = semanticModel.GetSymbolInfo(anonymousFunction, cancellationToken);
+ if (lambdaSymbolInfo.Symbol is not IMethodSymbol lambdaMethod)
+ return;
+
+ var invokedSymbolInfo = semanticModel.GetSymbolInfo(invokedExpression, cancellationToken);
+ if (invokedSymbolInfo.Symbol is not IMethodSymbol invokedMethod)
+ return;
+
+ // If we're calling a generic method, we have to have supplied type arguments. They cannot be inferred once
+ // we remove the arguments during simplification.
+ var invokedTypeArguments = invokedExpression.GetRightmostName() is GenericNameSyntax genericName
+ ? genericName.TypeArgumentList.Arguments
+ : default;
+
+ if (invokedMethod.TypeArguments.Length != invokedTypeArguments.Count)
+ return;
+
+ // Methods have to be complimentary. That means the same number of parameters, with proper
+ // co-contravariance for the parameters and return type.
+ if (lambdaMethod.Parameters.Length != invokedMethod.Parameters.Length)
+ return;
+
+ var compilation = semanticModel.Compilation;
+
+ // Must be able to convert the invoked method return type to the lambda's return type.
+ if (!IsIdentityOrImplicitConversion(compilation, invokedMethod.ReturnType, lambdaMethod.ReturnType))
+ return;
+
+ for (int i = 0, n = lambdaMethod.Parameters.Length; i < n; i++)
+ {
+ var lambdaParameter = lambdaMethod.Parameters[i];
+ var invokedParameter = invokedMethod.Parameters[i];
+
+ if (lambdaParameter.RefKind != invokedParameter.RefKind)
+ return;
+
+ // All the lambda parameters must be convertible to the invoked method parameters.
+ if (!IsIdentityOrImplicitConversion(compilation, lambdaParameter.Type, invokedParameter.Type))
+ return;
+ }
+
+ // Semantically, this looks good to go. Now, do an actual speculative replacement to ensure that the
+ // non-invoked method reference refers to the same method symbol, and that it converts to the same type that
+ // the lambda was.
+ var analyzer = new SpeculationAnalyzer(anonymousFunction, invokedExpression, semanticModel, cancellationToken);
+
+ var rewrittenExpression = analyzer.ReplacedExpression;
+ var rewrittenSemanticModel = analyzer.SpeculativeSemanticModel;
+
+ var rewrittenSymbolInfo = rewrittenSemanticModel.GetSymbolInfo(rewrittenExpression, cancellationToken);
+ if (rewrittenSymbolInfo.Symbol is not IMethodSymbol rewrittenMethod ||
+ !invokedMethod.Equals(rewrittenMethod))
+ {
+ return;
+ }
+
+ var rewrittenConvertedType = rewrittenSemanticModel.GetTypeInfo(rewrittenExpression, cancellationToken).ConvertedType;
+ if (!lambdaTypeInfo.ConvertedType.Equals(rewrittenConvertedType))
+ return;
+
+ if (OverloadsChanged(
+ semanticModel, anonymousFunction.GetRequiredParent(),
+ rewrittenSemanticModel, rewrittenExpression.GetRequiredParent(), cancellationToken))
+ {
+ return;
+ }
+
+ var startReportSpan = TextSpan.FromBounds(anonymousFunction.SpanStart, invokedExpression.SpanStart);
+ var endReportSpan = TextSpan.FromBounds(invokedExpression.Span.End, anonymousFunction.Span.End);
+
+ context.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
+ Descriptor,
+ syntaxTree.GetLocation(startReportSpan),
+ preference.Notification.Severity,
+ additionalLocations: ImmutableArray.Create(anonymousFunction.GetLocation()),
+ additionalUnnecessaryLocations: ImmutableArray.Create(
+ syntaxTree.GetLocation(startReportSpan),
+ syntaxTree.GetLocation(endReportSpan))));
+ }
+
+ private static bool OverloadsChanged(
+ SemanticModel semanticModel1,
+ SyntaxNode? node1,
+ SemanticModel semanticModel2,
+ SyntaxNode? node2,
+ CancellationToken cancellationToken)
+ {
+ while (node1 != null && node2 != null)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var method1 = semanticModel1.GetSymbolInfo(node1, cancellationToken).Symbol as IMethodSymbol;
+ var method2 = semanticModel2.GetSymbolInfo(node2, cancellationToken).Symbol as IMethodSymbol;
+
+ if (method1 is null != method2 is null)
+ return true;
+
+ if (method1 is not null && !method1.Equals(method2, SymbolEqualityComparer.IncludeNullability))
+ return true;
+
+ node1 = node1.Parent;
+ node2 = node2.Parent;
+ }
+
+ return false;
+ }
+
+ private static bool IsIdentityOrImplicitConversion(Compilation compilation, ITypeSymbol type1, ITypeSymbol type2)
+ {
+ // Dynamic can have an identity conversion between types. But it can have a very different effect on the
+ // generated code. Do not allow the change if these are not in agreement.
+ if (type1 is IDynamicTypeSymbol != type2 is IDynamicTypeSymbol)
+ return false;
+
+ var conversion = compilation.ClassifyConversion(type1, type2);
+ return conversion.IsIdentityOrImplicitReference();
+ }
+
+ private static bool MayHaveSideEffects(ExpressionSyntax expression)
+ {
+ // Checks to see if the expression being invoked looks side-effect free. If so, changing from executing
+ // each time in the lambda to only executing it once could have impact on the program.
+
+ return !expression.DescendantNodesAndSelf().All(
+ n => n is TypeSyntax or
+ TypeArgumentListSyntax or
+ MemberAccessExpressionSyntax or
+ InstanceExpressionSyntax or
+ LiteralExpressionSyntax);
+ }
+
+ private static SeparatedSyntaxList GetParameters(AnonymousFunctionExpressionSyntax expression)
+ => expression switch
+ {
+ AnonymousMethodExpressionSyntax anonymousMethod => anonymousMethod.ParameterList?.Parameters ?? default,
+ SimpleLambdaExpressionSyntax simpleLambda => SyntaxFactory.SingletonSeparatedList(simpleLambda.Parameter),
+ ParenthesizedLambdaExpressionSyntax parenthesizedLambda => parenthesizedLambda.ParameterList.Parameters,
+ _ => throw ExceptionUtilities.UnexpectedValue(expression.Kind()),
+ };
+
+ public static bool TryGetAnonymousFunctionInvocation(
+ AnonymousFunctionExpressionSyntax anonymousFunction,
+ [NotNullWhen(true)] out InvocationExpressionSyntax? invocation,
+ out bool wasAwaited)
+ {
+ if (anonymousFunction.ExpressionBody != null)
+ return TryGetInvocation(anonymousFunction.ExpressionBody, out invocation, out wasAwaited);
+
+ if (anonymousFunction.Block != null && anonymousFunction.Block.Statements.Count == 1)
+ {
+ var statement = anonymousFunction.Block.Statements[0];
+ if (statement is ReturnStatementSyntax { Expression: { } expression })
+ return TryGetInvocation(expression, out invocation, out wasAwaited);
+
+ if (statement is ExpressionStatementSyntax expressionStatement)
+ return TryGetInvocation(expressionStatement.Expression, out invocation, out wasAwaited);
+ }
+
+ invocation = null;
+ wasAwaited = false;
+ return false;
+ }
+
+ private static bool TryGetInvocation(
+ ExpressionSyntax expression,
+ [NotNullWhen(true)] out InvocationExpressionSyntax? invocation,
+ out bool wasAwaited)
+ {
+ wasAwaited = false;
+
+ // if we have either `await Goo()` or `await Goo().ConfigureAwait` then unwrap to get at `Goo()`.
+ if (expression is AwaitExpressionSyntax awaitExpression)
+ {
+ wasAwaited = true;
+ expression = awaitExpression.Expression;
+ if (expression is InvocationExpressionSyntax
+ {
+ Expression: MemberAccessExpressionSyntax { Name.Identifier.ValueText: nameof(Task.ConfigureAwait), Expression: var underlying }
+ })
+ {
+ expression = underlying;
+ }
+ }
+
+ invocation = expression as InvocationExpressionSyntax;
+ return invocation != null;
+ }
+ }
+}
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
index 60ed538229495..4d8bb9ec8187a 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf
@@ -67,6 +67,11 @@
Vložená deklarace proměnné
+
+
+ Lambda expression can be removed
+
+
Nesprávné umístění direktivy using
@@ -122,6 +127,11 @@
Odebrat operátory potlačení
+
+
+ Remove unnecessary lambda expression
+
+
Odebrat nepotřebný operátor potlačení
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
index 13a8c62f2c15b..4cc936f9ba175 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf
@@ -67,6 +67,11 @@
Inlinevariablendeklaration
+
+
+ Lambda expression can be removed
+
+
Die using-Anweisung wurde falsch platziert.
@@ -122,6 +127,11 @@
Unterdrückungsoperatoren entfernen
+
+
+ Remove unnecessary lambda expression
+
+
Unnötigen Unterdrückungsoperator entfernen
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
index 4555025e70ad8..382ef105eac47 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf
@@ -67,6 +67,11 @@
Declaración de variables alineada
+
+
+ Lambda expression can be removed
+
+
Directiva using mal colocada
@@ -122,6 +127,11 @@
Quitar operadores de supresión
+
+
+ Remove unnecessary lambda expression
+
+
Quitar operador de supresión innecesario
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
index a8a5ac05e3c33..2d1104f4cd7ef 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf
@@ -67,6 +67,11 @@
Déclaration de variable inline
+
+
+ Lambda expression can be removed
+
+
Directive using mal placée
@@ -122,6 +127,11 @@
Supprimer les opérateurs de suppression
+
+
+ Remove unnecessary lambda expression
+
+
Supprimer l'opérateur de suppression inutile
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
index 0b436eb14578b..9fbaa5533fc67 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf
@@ -67,6 +67,11 @@
Dichiarazione di variabile inline
+
+
+ Lambda expression can be removed
+
+
Direttiva using in posizione errata
@@ -122,6 +127,11 @@
Rimuovi gli operatori di eliminazione
+
+
+ Remove unnecessary lambda expression
+
+
Rimuovi l'operatore di eliminazione non necessario
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
index 364e8205812a2..7172a70422f65 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf
@@ -67,6 +67,11 @@
インライン変数宣言
+
+
+ Lambda expression can be removed
+
+
using ディレクティブが正しく配置されていません
@@ -122,6 +127,11 @@
抑制演算子の削除
+
+
+ Remove unnecessary lambda expression
+
+
不要な抑制演算子を削除します
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
index bfb8d961fe9ab..b19b442b7667e 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf
@@ -67,6 +67,11 @@
인라인 변수 선언
+
+
+ Lambda expression can be removed
+
+
위치가 잘못된 using 지시문
@@ -122,6 +127,11 @@
비표시 오류(Suppression) 연산자 제거
+
+
+ Remove unnecessary lambda expression
+
+
불필요한 비표시 오류(Suppression) 연산자 제거
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
index ecac3f0c4cb4f..f8db95470a852 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf
@@ -67,6 +67,11 @@
Deklaracja zmiennej wbudowanej
+
+
+ Lambda expression can be removed
+
+
Nieprawidłowo umieszczona dyrektywa using
@@ -122,6 +127,11 @@
Usuń operatory pomijania
+
+
+ Remove unnecessary lambda expression
+
+
Usuń niepotrzebny operator pomijania
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
index a06f12ba0e9b9..9995149d5537c 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf
@@ -67,6 +67,11 @@
Declaração de variável embutida
+
+
+ Lambda expression can be removed
+
+
Diretiva using em local incorreto
@@ -122,6 +127,11 @@
Remover operadores de supressão
+
+
+ Remove unnecessary lambda expression
+
+
Remover operador de supressão desnecessário
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
index fa246d6762cec..b3bac6215c6fd 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf
@@ -67,6 +67,11 @@
Объявление встроенной переменной
+
+
+ Lambda expression can be removed
+
+
Неправильно расположенная директива using
@@ -122,6 +127,11 @@
Удалить операторы подавления
+
+
+ Remove unnecessary lambda expression
+
+
Удалить ненужный оператор подавления
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
index bd40af1e443e9..edb1e34ddf413 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf
@@ -67,6 +67,11 @@
Satır içi değişken bildirimi
+
+
+ Lambda expression can be removed
+
+
Yanlış yerleştirilmiş using yönergesi
@@ -122,6 +127,11 @@
Gizleme işleçlerini kaldır
+
+
+ Remove unnecessary lambda expression
+
+
Gereksiz gizleme işlecini kaldır
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
index e6d71376160ef..09adb06f36210 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf
@@ -67,6 +67,11 @@
内联变量声明
+
+
+ Lambda expression can be removed
+
+
错放了 using 指令
@@ -122,6 +127,11 @@
请删除忽略运算符
+
+
+ Remove unnecessary lambda expression
+
+
请删除不必要的忽略运算符
diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
index eb0e3857ae079..06eaa74caeefb 100644
--- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
+++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf
@@ -67,6 +67,11 @@
內嵌變數宣告
+
+
+ Lambda expression can be removed
+
+
using 指示詞位置錯誤
@@ -122,6 +127,11 @@
移除隱藏的運算子
+
+
+ Remove unnecessary lambda expression
+
+
移除隱藏運算子中不需要的運算子
diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
index d411389a4b28d..720560e502c03 100644
--- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
+++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems
@@ -36,6 +36,7 @@
+
diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs
new file mode 100644
index 0000000000000..d5f32eb3fb2bc
--- /dev/null
+++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression
+{
+ using static CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer;
+
+ [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryLambdaExpression), Shared]
+ internal partial class CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider : SyntaxEditorBasedCodeFixProvider
+ {
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider()
+ {
+ }
+
+ public override ImmutableArray FixableDiagnosticIds
+ => ImmutableArray.Create(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId);
+
+ internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle;
+
+ public override Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ context.RegisterCodeFix(
+ new MyCodeAction(c => FixAsync(context.Document, context.Diagnostics.First(), c)),
+ context.Diagnostics);
+ return Task.CompletedTask;
+ }
+
+ protected override Task FixAllAsync(
+ Document document, ImmutableArray diagnostics,
+ SyntaxEditor editor, CancellationToken cancellationToken)
+ {
+ foreach (var diagnostic in diagnostics)
+ {
+ var anonymousFunction = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken);
+
+ editor.ReplaceNode(anonymousFunction,
+ (current, generator) =>
+ {
+ if (current is AnonymousFunctionExpressionSyntax anonymousFunction &&
+ TryGetAnonymousFunctionInvocation(anonymousFunction, out var invocation, out _))
+ {
+ return invocation.Expression.WithTriviaFrom(current).Parenthesize();
+ }
+
+ return current;
+ });
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private class MyCodeAction : CustomCodeActions.DocumentChangeAction
+ {
+ public MyCodeAction(Func> createChangedDocument)
+ : base(CSharpAnalyzersResources.Remove_unnecessary_lambda_expression, createChangedDocument, nameof(CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider))
+ {
+ }
+ }
+ }
+}
diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
index 4f5e6d27eedfe..bf6a967e39024 100644
--- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
+++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs
@@ -76,6 +76,7 @@ internal static class EnforceOnBuildValues
public const EnforceOnBuild UseImplicitObjectCreation = /*IDE0090*/ EnforceOnBuild.Recommended;
public const EnforceOnBuild RemoveRedundantEquality = /*IDE0100*/ EnforceOnBuild.Recommended;
public const EnforceOnBuild RemoveUnnecessaryDiscardDesignation = /*IDE0110*/ EnforceOnBuild.Recommended;
+ public const EnforceOnBuild RemoveUnnecessaryLambdaExpression = /*IDE0200*/ EnforceOnBuild.Recommended;
public const EnforceOnBuild InvokeDelegateWithConditionalAccess = /*IDE1005*/ EnforceOnBuild.Recommended;
public const EnforceOnBuild NamingRule = /*IDE1006*/ EnforceOnBuild.Recommended;
public const EnforceOnBuild MatchFolderAndNamespace = /*IDE0130*/ EnforceOnBuild.Recommended;
diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
index 592390e6f38ab..364fb3468a509 100644
--- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
+++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs
@@ -169,6 +169,8 @@ internal static class IDEDiagnosticIds
public const string UseParameterNullCheckingId = "IDE0190";
+ public const string RemoveUnnecessaryLambdaExpressionDiagnosticId = "IDE0200";
+
// 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 e84bd13bf549f..94eec78057a11 100644
--- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs
+++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs
@@ -91,6 +91,7 @@ internal static class PredefinedCodeFixProviderNames
public const string RemoveUnnecessaryCast = nameof(RemoveUnnecessaryCast);
public const string RemoveUnnecessaryDiscardDesignation = nameof(RemoveUnnecessaryDiscardDesignation);
public const string RemoveUnnecessaryImports = nameof(RemoveUnnecessaryImports);
+ public const string RemoveUnnecessaryLambdaExpression = nameof(RemoveUnnecessaryLambdaExpression);
public const string RemoveUnnecessaryParentheses = nameof(RemoveUnnecessaryParentheses);
public const string RemoveUnnecessaryPragmaSuppressions = nameof(RemoveUnnecessaryPragmaSuppressions);
public const string RemoveUnreachableCode = nameof(RemoveUnreachableCode);
diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs
index cb0992ea28c49..444e63dd244b1 100644
--- a/src/Compilers/Test/Core/Traits/Traits.cs
+++ b/src/Compilers/Test/Core/Traits/Traits.cs
@@ -118,7 +118,7 @@ public static class Features
public const string CodeActionsInvertIf = "CodeActions.InvertIf";
public const string CodeActionsInvertLogical = "CodeActions.InvertLogical";
public const string CodeActionsInvokeDelegateWithConditionalAccess = "CodeActions.InvokeDelegateWithConditionalAccess";
- public const string CodeActionsLambdaSimplifier = "CodeActions.LambdaSimplifier";
+ public const string CodeActionsRemoveUnnecessaryLambdaExpression = "CodeActions.RemoveUnnecessaryLambdaExpression";
public const string CodeActionsMakeFieldReadonly = "CodeActions.MakeFieldReadonly";
public const string CodeActionsMakeLocalFunctionStatic = "CodeActions.MakeLocalFunctionStatic";
public const string CodeActionsMakeMethodAsynchronous = "CodeActions.MakeMethodAsynchronous";
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/LambdaSimplifier/LambdaSimplifierTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/LambdaSimplifier/LambdaSimplifierTests.cs
deleted file mode 100644
index 365d272454b37..0000000000000
--- a/src/EditorFeatures/CSharpTest/CodeActions/LambdaSimplifier/LambdaSimplifierTests.cs
+++ /dev/null
@@ -1,643 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#nullable disable
-
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.CodeRefactorings;
-using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.LambdaSimplifier;
-using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
-using Microsoft.CodeAnalysis.Test.Utilities;
-using Roslyn.Test.Utilities;
-using Xunit;
-
-namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.LambdaSimplifier
-{
- public class LambdaSimplifierTests : AbstractCSharpCodeActionTest
- {
- protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
- => new LambdaSimplifierCodeRefactoringProvider();
-
- [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsLambdaSimplifier)]
- public async Task TestFixAll1()
- {
- await TestInRegularAndScriptAsync(
-@"using System;
-
-class C
-{
- void Goo()
- {
- Bar(s [||]=> Quux(s));
- }
-
- void Bar(Func f);
- string Quux(int i);
-}",
-@"using System;
-
-class C
-{
- void Goo()
- {
- Bar(Quux);
- }
-
- void Bar(Func f);
- string Quux(int i);
-}",
- index: 1);
- }
-
- [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsLambdaSimplifier)]
- public async Task TestFixCoContravariance1()
- {
- await TestInRegularAndScriptAsync(
-@"using System;
-
-class C
-{
- void Goo()
- {
- Bar(s [||]=> Quux(s));
- }
-
- void Bar(Func
+
+
+ Prefer method group conversion
+
+
Preferovat strukturu oboru názvů a shody složek
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
index 529459b546735..9214803885316 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf
@@ -992,6 +992,11 @@
Indexoperator bevorzugen
+
+
+ Prefer method group conversion
+
+
Namespace- und Ordnerübereinstimmungsstruktur bevorzugen
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
index 1b36dfb32fe2d..95bdc345a9c42 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf
@@ -992,6 +992,11 @@
Preferir operador de índice
+
+
+ Prefer method group conversion
+
+
Preferir estructura de coincidencia de espacio de nombres y carpetas
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
index bc379136aefad..c2f98dd332ac2 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf
@@ -992,6 +992,11 @@
Préférer l'opérateur d'index
+
+
+ Prefer method group conversion
+
+
Préférer l’espace de noms et la structure de correspondance de dossier
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
index dd280670703ec..7e0d381778a7a 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf
@@ -992,6 +992,11 @@
Preferisci operatore di indice
+
+
+ Prefer method group conversion
+
+
Preferire la struttura di corrispondenza spazio dei nomi e cartella
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
index 2b00fdf122d0c..b3100a936ca46 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf
@@ -992,6 +992,11 @@
インデックス演算子を優先
+
+
+ Prefer method group conversion
+
+
名前空間とフォルダー一致構造を優先する
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
index 2b2812a9b0524..1db9539ee2c27 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf
@@ -992,6 +992,11 @@
인덱스 연산자 선호
+
+
+ Prefer method group conversion
+
+
네임스페이스 및 폴더 일치 구조 선호
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
index 0b0dd74607534..a143d4c773a03 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf
@@ -992,6 +992,11 @@
Preferuj operator indeksowania
+
+
+ Prefer method group conversion
+
+
Preferuj strukturę dopasowania przestrzeni nazw i folderów
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
index f52ec924844f0..4197537e2a0d9 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf
@@ -992,6 +992,11 @@
Preferir operador de índice
+
+
+ Prefer method group conversion
+
+
Prefira espaço de nomes e estrutura de correspondência de pasta
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
index 91a2006438203..19f9a6da16b0a 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf
@@ -992,6 +992,11 @@
Предпочитать оператор index
+
+
+ Prefer method group conversion
+
+
Предпочитать пространство имен и структуру совпадений папок
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
index 6830da5ab9ac3..b85e07a628f81 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf
@@ -992,6 +992,11 @@
Dizin işlecini tercih et
+
+
+ Prefer method group conversion
+
+
Ad alanı ve klasör eşleşme yapısını 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 34c3d97eda05d..c2ac3476cd0d5 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf
@@ -992,6 +992,11 @@
首选索引运算符
+
+
+ Prefer method group conversion
+
+
首选命名空间和文件夹匹配结构
diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
index 5f3d5d4b33555..4ec614b139bc2 100644
--- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
+++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf
@@ -992,6 +992,11 @@
優先使用索引運算子
+
+
+ Prefer method group conversion
+
+
優先使用命名空間和資料夾相符結構
diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
index 0fd050a669a7b..9cd8b5dd4b538 100644
--- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
+++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb
@@ -128,6 +128,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter
csharp_prefer_braces = true
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = block_scoped
+csharp_style_prefer_method_group_conversion = true
# Expression-level preferences
csharp_prefer_simple_default_expression = true
@@ -362,6 +363,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter
csharp_prefer_braces = true
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = block_scoped
+csharp_style_prefer_method_group_conversion = true
# Expression-level preferences
csharp_prefer_simple_default_expression = true
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
index a9430fceb2571..0eda8223d9e16 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs
@@ -339,6 +339,12 @@ private static Option2> CreateN
new(NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Silent),
"csharp_style_namespace_declarations");
+ public static readonly Option2> PreferMethodGroupConversion = CreateOption(
+ CSharpCodeStyleOptionGroups.CodeBlockPreferences, nameof(PreferMethodGroupConversion),
+ defaultValue: s_trueWithSilentEnforcement,
+ "csharp_style_prefer_method_group_conversion",
+ "TextEditor.CSharp.Specific.PreferMethodGroupConversion");
+
#if false
public static readonly Option2> VarElsewhere = CreateOption(
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs
index b84c4abd638c9..3b81b9c6ed3d5 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs
@@ -6,6 +6,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Shared.Extensions
{
internal static class LanguageVersionExtensions
{
+ public static bool IsCSharp11OrAbove(this LanguageVersion languageVersion)
+ => languageVersion >= LanguageVersion.Preview;
+
public static bool HasConstantInterpolatedStrings(this LanguageVersion languageVersion)
=> languageVersion >= LanguageVersion.CSharp10;
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs
index 3391a170fdc94..9c0544538f439 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs
@@ -355,5 +355,8 @@ public ImmutableArray GetLocalFunctionSymbols(Compilation compila
return builder.ToImmutable();
}
+
+ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken)
+ => node.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken);
}
}
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs
index 68e7015031163..b02ce80d860fa 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs
@@ -99,5 +99,7 @@ internal partial interface ISemanticFacts
/// Finds all local function definitions within the syntax references for a given
///
ImmutableArray GetLocalFunctionSymbols(Compilation compilation, ISymbol symbol, CancellationToken cancellationToken);
+
+ bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken);
}
}
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb
index ddc82bff9c78e..792810f70c4ea 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb
@@ -242,5 +242,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Function GetLocalFunctionSymbols(compilation As Compilation, symbol As ISymbol, cancellationToken As CancellationToken) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFacts.GetLocalFunctionSymbols
Return ImmutableArray(Of IMethodSymbol).Empty
End Function
+
+ Public Function IsInExpressionTree(semanticModel As SemanticModel, node As SyntaxNode, expressionTypeOpt As INamedTypeSymbol, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInExpressionTree
+ Return node.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken)
+ End Function
End Class
End Namespace
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs
index 0665a2f2b99eb..9e0299091bcf9 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs
@@ -75,9 +75,6 @@ public bool IsExpressionContext(SemanticModel semanticModel, int position, Cance
attributes: true, cancellationToken: cancellationToken, semanticModelOpt: semanticModel);
}
- public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken)
- => node.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken);
-
public bool IsStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
{
return semanticModel.SyntaxTree.IsStatementContext(
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs
index d827973246a5b..3a4125bd00b3e 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs
@@ -162,6 +162,10 @@ public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode nod
public ImmutableArray GetLocalFunctionSymbols(Compilation compilation, ISymbol symbol, CancellationToken cancellationToken)
=> SemanticFacts.GetLocalFunctionSymbols(compilation, symbol, cancellationToken);
+
+ public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken)
+ => SemanticFacts.IsInExpressionTree(semanticModel, node, expressionTypeOpt, cancellationToken);
+
#endregion
}
}
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs
index 04ec70a31cda9..97260b5d15584 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs
@@ -25,8 +25,6 @@ internal partial interface ISemanticFactsService : ISemanticFacts, ILanguageServ
bool IsLabelContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsAttributeNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
- bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken);
-
SyntaxToken GenerateUniqueName(
SemanticModel semanticModel, SyntaxNode location,
SyntaxNode containerOpt, string baseName, CancellationToken cancellationToken);
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb
index 467065ec27d6b..5bee2fee605dc 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb
@@ -50,10 +50,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return semanticModel.SyntaxTree.IsExpressionContext(position, token, cancellationToken, semanticModel)
End Function
- Public Function IsInExpressionTree(semanticModel As SemanticModel, node As SyntaxNode, expressionTypeOpt As INamedTypeSymbol, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsInExpressionTree
- Return node.IsInExpressionTree(semanticModel, expressionTypeOpt, cancellationToken)
- End Function
-
Public Function IsMemberDeclarationContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsMemberDeclarationContext
Dim token = semanticModel.SyntaxTree.GetTargetToken(position, cancellationToken)
Return semanticModel.SyntaxTree.IsInterfaceMemberDeclarationKeywordContext(position, token, cancellationToken) OrElse