public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); InvocationExpressionSyntax invocation = root .FindNode(context.Span, getInnermostNodeForTie: true)? .FirstAncestorOrSelf <InvocationExpressionSyntax>(); foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.SimplifyLinqMethodChain: { var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression; switch (memberAccess.Name.Identifier.ValueText) { case "Cast": { CodeAction codeAction = CodeAction.Create( "Simplify method chain", cancellationToken => CallOfTypeInsteadOfWhereAndCastRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } default: { CodeAction codeAction = CodeAction.Create( "Simplify method chain", cancellationToken => SimplifyLinqMethodChainRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } } break; } case DiagnosticIdentifiers.CombineEnumerableWhereMethodChain: { CodeAction codeAction = CodeAction.Create( "Combine 'Where' method chain", cancellationToken => CombineEnumerableWhereMethodChainRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfAnyMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Any'", cancellationToken => UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.RefactorAsync(context.Document, invocation, propertyName, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfCountMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Count'", cancellationToken => UseCountOrLengthPropertyInsteadOfCountMethodRefactoring.RefactorAsync(context.Document, invocation, diagnostic.Properties["PropertyName"], cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseBitwiseOperationInsteadOfCallingHasFlag: { CodeAction codeAction = CodeAction.Create( UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.Title, cancellationToken => UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantToStringCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToString' call", cancellationToken => RemoveRedundantCallRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCastMethodInsteadOfSelectMethod: { CodeAction codeAction = CodeAction.Create( "Call 'Cast' instead of 'Select'", cancellationToken => UseCastMethodInsteadOfSelectMethodRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantStringToCharArrayCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToCharArray' call", cancellationToken => RemoveRedundantCallRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallFindMethodInsteadOfFirstOrDefaultMethod: { CodeAction codeAction = CodeAction.Create( "Call 'Find' instead of 'FirstOrDefault'", cancellationToken => CallFindMethodInsteadOfFirstOrDefaultMethodRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfElementAt: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfElementAtRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfFirst: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'First'", cancellationToken => UseElementAccessInsteadOfFirstRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallStringConcatInsteadOfStringJoin: { CodeAction codeAction = CodeAction.Create( "Call 'Concat' instead of 'Join'", cancellationToken => CallStringConcatInsteadOfStringJoinRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), diagnostic.Id + EquivalenceKeySuffix); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocation = (InvocationExpressionSyntax)context.Node; ExpressionSyntax expression = invocation.Expression; if (expression?.IsKind(SyntaxKind.SimpleMemberAccessExpression) == true) { var memberAccess = (MemberAccessExpressionSyntax)expression; ArgumentListSyntax argumentList = invocation.ArgumentList; if (argumentList?.IsMissing == false) { int argumentCount = argumentList.Arguments.Count; string methodName = memberAccess.Name?.Identifier.ValueText; if (argumentCount == 0) { switch (methodName) { case "Any": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "Cast": { CallOfTypeInsteadOfWhereAndCastRefactoring.Analyze(context, invocation); break; } case "Count": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); UseInsteadOfCountMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "First": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); UseElementAccessInsteadOfFirstRefactoring.Analyze(context, invocation, memberAccess); break; } case "FirstOrDefault": case "Last": case "LastOrDefault": case "LongCount": case "Single": case "SingleOrDefault": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); break; } } } else if (argumentCount == 1) { switch (methodName) { case "ElementAt": { UseElementAccessInsteadOfElementAtRefactoring.Analyze(context, invocation, memberAccess); break; } case "FirstOrDefault": { CallFindMethodInsteadOfFirstOrDefaultMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "Select": { UseCastMethodInsteadOfSelectMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "Where": { CombineEnumerableWhereMethodChainRefactoring.Analyze(context, invocation, memberAccess); break; } } } } } if (UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.CanRefactor(invocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag, invocation); } RemoveRedundantToStringCallRefactoring.Analyze(context, invocation); RemoveRedundantStringToCharArrayCallRefactoring.Analyze(context, invocation); }
private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocation = (InvocationExpressionSyntax)context.Node; ExpressionSyntax expression = invocation.Expression; if (expression?.IsKind(SyntaxKind.SimpleMemberAccessExpression) == true) { var memberAccess = (MemberAccessExpressionSyntax)expression; ArgumentListSyntax argumentList = invocation.ArgumentList; if (argumentList?.IsMissing == false) { int argumentCount = argumentList.Arguments.Count; string methodName = memberAccess.Name?.Identifier.ValueText; if (argumentCount == 0) { switch (methodName) { case "Any": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "Cast": { CallOfTypeInsteadOfWhereAndCastRefactoring.Analyze(context, invocation); break; } case "Count": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); UseInsteadOfCountMethodRefactoring.Analyze(context, invocation, memberAccess); break; } case "First": case "FirstOrDefault": case "Last": case "LastOrDefault": case "LongCount": case "Single": case "SingleOrDefault": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); break; } } } else if (argumentCount == 1) { switch (methodName) { case "FirstOrDefault": { CallFindInsteadOfFirstOrDefaultRefactoring.Analyze(context, invocation, memberAccess); break; } case "Where": { CombineEnumerableWhereMethodChainRefactoring.Analyze(context, invocation, memberAccess); break; } } } } } if (UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.CanRefactor(invocation, context.SemanticModel, context.CancellationToken) && !invocation.SpanContainsDirectives()) { context.ReportDiagnostic(DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag, invocation); } RemoveRedundantStringToCharArrayCallRefactoring.Analyze(context, invocation); CombineEnumerableWhereAndAnyRefactoring.AnalyzeInvocationExpression(context); if (!invocation.ContainsDiagnostics) { if (!invocation.SpanContainsDirectives()) { CallExtensionMethodAsInstanceMethodRefactoring.AnalysisResult result = CallExtensionMethodAsInstanceMethodRefactoring.Analyze(invocation, context.SemanticModel, context.CancellationToken); if (result.Success && context.SemanticModel .GetEnclosingNamedType(result.InvocationExpression.SpanStart, context.CancellationToken)? .Equals(result.MethodSymbol.ContainingType) == false) { context.ReportDiagnostic(DiagnosticDescriptors.CallExtensionMethodAsInstanceMethod, invocation); } } MemberInvocationExpression memberInvocation; if (MemberInvocationExpression.TryCreate(invocation, out memberInvocation)) { if (!invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodRefactoring.Analyze(context, memberInvocation); } string methodName = memberInvocation.NameText; AvoidNullReferenceExceptionRefactoring.Analyze(context, memberInvocation); int argumentCount = memberInvocation.ArgumentList.Arguments.Count; switch (argumentCount) { case 0: { switch (methodName) { case "First": { if (!memberInvocation.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessInsteadOfFirstRefactoring.CanRefactor(memberInvocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseElementAccessInsteadOfFirst, memberInvocation.Name); } break; } case "ToString": { RemoveRedundantToStringCallRefactoring.Analyze(context, memberInvocation); break; } } break; } case 1: { switch (methodName) { case "ElementAt": { if (!memberInvocation.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessInsteadOfElementAtRefactoring.CanRefactor(memberInvocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseElementAccessInsteadOfElementAt, memberInvocation.Name); } break; } } break; } } switch (methodName) { case "Append": case "AppendLine": case "AppendFormat": case "Insert": { OptimizeStringBuilderAppendCallRefactoring.Analyze(context, memberInvocation); break; } case "Select": { if (argumentCount == 1 || argumentCount == 2) { CallCastInsteadOfSelectRefactoring.Analyze(context, memberInvocation); } break; } case "OrderBy": case "OrderByDescending": { if (argumentCount == 1 || argumentCount == 2 || argumentCount == 3) { CallThenByInsteadOfOrderByRefactoring.Analyze(context, memberInvocation); } break; } } if (UseMethodChainingRefactoring.IsFixable(memberInvocation, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseMethodChaining, memberInvocation.InvocationExpression); } } } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out InvocationExpressionSyntax invocation)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.CombineEnumerableWhereMethodChain: { CodeAction codeAction = CodeAction.Create( "Combine 'Where' method chain", cancellationToken => CombineEnumerableWhereMethodChainRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfAnyMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Any'", cancellationToken => UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.RefactorAsync(context.Document, invocation, propertyName, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfCountMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Count'", cancellationToken => UseCountOrLengthPropertyInsteadOfCountMethodRefactoring.RefactorAsync(context.Document, invocation, diagnostic.Properties["PropertyName"], cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseBitwiseOperationInsteadOfCallingHasFlag: { CodeAction codeAction = CodeAction.Create( UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.Title, cancellationToken => UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantToStringCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToString' call", cancellationToken => context.Document.ReplaceNodeAsync(invocation, RefactoringUtility.RemoveInvocation(invocation).WithFormatterAnnotation(), cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallCastInsteadOfSelect: { CodeAction codeAction = CodeAction.Create( "Call 'Cast' instead of 'Select'", cancellationToken => CallCastInsteadOfSelectRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantStringToCharArrayCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToCharArray' call", cancellationToken => context.Document.ReplaceNodeAsync(invocation, RefactoringUtility.RemoveInvocation(invocation).WithFormatterAnnotation(), cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallFindInsteadOfFirstOrDefault: { CodeAction codeAction = CodeAction.Create( "Call 'Find' instead of 'FirstOrDefault'", cancellationToken => CallFindInsteadOfFirstOrDefaultRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfElementAt: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfElementAtRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfFirst: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'First'", cancellationToken => UseElementAccessInsteadOfFirstRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallStringConcatInsteadOfStringJoin: { CodeAction codeAction = CodeAction.Create( "Call 'Concat' instead of 'Join'", cancellationToken => CallStringConcatInsteadOfStringJoinRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallDebugFailInsteadOfDebugAssert: { CodeAction codeAction = CodeAction.Create( "Call 'Fail' instead of 'Assert'", cancellationToken => CallDebugFailInsteadOfDebugAssertRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallExtensionMethodAsInstanceMethod: { CodeAction codeAction = CodeAction.Create( CallExtensionMethodAsInstanceMethodRefactoring.Title, cancellationToken => CallExtensionMethodAsInstanceMethodRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallThenByInsteadOfOrderBy: { SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocation); string oldName = invocationInfo.NameText; string newName = (string.Equals(oldName, "OrderBy", StringComparison.Ordinal)) ? "ThenBy" : "ThenByDescending"; CodeAction codeAction = CodeAction.Create( $"Call '{newName}' instead of '{oldName}'", cancellationToken => CallThenByInsteadOfOrderByRefactoring.RefactorAsync(context.Document, invocation, newName, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }