예제 #1
0
        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;
                }
                }
            }
        }
예제 #2
0
        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;
                }
                }
            }
        }