public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsAnyCodeFixEnabled(
                    CodeFixIdentifiers.AddComparisonWithBooleanLiteral,
                    CodeFixIdentifiers.CreateSingletonArray,
                    CodeFixIdentifiers.UseUncheckedExpression,
                    CodeFixIdentifiers.RemoveConstModifier,
                    CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            var expression = root.FindNode(context.Span, getInnermostNodeForTie: true) as ExpressionSyntax;

            Debug.Assert(expression != null, $"{nameof(expression)} is null");

            if (expression == null)
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists:
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken);

                    ITypeSymbol type          = typeInfo.Type;
                    ITypeSymbol convertedType = typeInfo.ConvertedType;

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddComparisonWithBooleanLiteral) &&
                        type?.IsNullableOf(SpecialType.System_Boolean) == true)
                    {
                        if (convertedType?.IsBoolean() == true ||
                            AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression))
                        {
                            CodeAction codeAction = CodeAction.Create(
                                AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression),
                                cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken),
                                GetEquivalenceKey(diagnostic));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) &&
                        type?.IsErrorType() == false &&
                        !type.Equals(convertedType) &&
                        convertedType.IsArrayType())
                    {
                        var arrayType = (IArrayTypeSymbol)convertedType;

                        if (semanticModel.IsImplicitConversion(expression, arrayType.ElementType))
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Create singleton array",
                                cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseUncheckedExpression))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Use 'unchecked'",
                        cancellationToken =>
                        {
                            CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia());

                            newNode = newNode.WithTriviaFrom(expression);

                            return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConstModifier))
                    {
                        break;
                    }

                    LocalDeclarationStatementSyntax localDeclarationStatement = GetLocalDeclarationStatement(expression);

                    if (localDeclarationStatement == null)
                    {
                        break;
                    }

                    SyntaxTokenList modifiers = localDeclarationStatement.Modifiers;

                    if (!modifiers.Contains(SyntaxKind.ConstKeyword))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove 'const' modifier",
                        cancellationToken =>
                        {
                            LocalDeclarationStatementSyntax newNode = localDeclarationStatement.RemoveModifier(SyntaxKind.ConstKeyword);

                            return(context.Document.ReplaceNodeAsync(localDeclarationStatement, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ITypeSymbol typeSymbol = semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType;

                    if (typeSymbol?.SupportsExplicitDeclaration() == true)
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Replace 'null' with default value",
                            cancellationToken =>
                            {
                                ExpressionSyntax newNode = typeSymbol.ToDefaultValueSyntax(semanticModel, expression.SpanStart);

                                return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken));
                            },
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }
                }
            }
        }
Exemple #2
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddComparisonWithBooleanLiteral) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseUncheckedExpression) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConstModifier) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseCoalesceExpression) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse))
            {
                return;
            }

            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindNode(root, context.Span, out ExpressionSyntax expression))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists:
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken);

                    ITypeSymbol type          = typeInfo.Type;
                    ITypeSymbol convertedType = typeInfo.ConvertedType;

                    if (type?.IsNamedType() == true)
                    {
                        var namedType = (INamedTypeSymbol)type;

                        if (namedType.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
                        {
                            if (convertedType?.IsBoolean() == true ||
                                AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression))
                            {
                                if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddComparisonWithBooleanLiteral))
                                {
                                    CodeAction codeAction = CodeAction.Create(
                                        AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression),
                                        cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken),
                                        GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddComparisonWithBooleanLiteral));

                                    context.RegisterCodeFix(codeAction, diagnostic);
                                }
                            }
                            else if (namedType.TypeArguments[0].Equals(convertedType))
                            {
                                if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseCoalesceExpression))
                                {
                                    CodeAction codeAction = CodeAction.Create(
                                        "Use coalesce expression",
                                        cancellationToken =>
                                        {
                                            ExpressionSyntax defaultValue = convertedType.ToDefaultValueSyntax(semanticModel, expression.SpanStart);

                                            ExpressionSyntax newNode = CoalesceExpression(expression.WithoutTrivia(), defaultValue)
                                                                       .WithTriviaFrom(expression)
                                                                       .Parenthesize()
                                                                       .WithFormatterAnnotation();

                                            return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken));
                                        },
                                        GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseCoalesceExpression));

                                    context.RegisterCodeFix(codeAction, diagnostic);
                                }
                            }
                        }
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) &&
                        type?.IsErrorType() == false &&
                        !type.Equals(convertedType) &&
                        convertedType.IsArrayType())
                    {
                        var arrayType = (IArrayTypeSymbol)convertedType;

                        if (semanticModel.IsImplicitConversion(expression, arrayType.ElementType))
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Create singleton array",
                                cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseUncheckedExpression))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Use 'unchecked'",
                        cancellationToken =>
                        {
                            CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia());

                            newNode = newNode.WithTriviaFrom(expression);

                            return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConstModifier))
                    {
                        break;
                    }

                    LocalDeclarationStatementSyntax localDeclarationStatement = GetLocalDeclarationStatement(expression);

                    if (localDeclarationStatement == null)
                    {
                        break;
                    }

                    SyntaxTokenList modifiers = localDeclarationStatement.Modifiers;

                    if (!modifiers.Contains(SyntaxKind.ConstKeyword))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove 'const' modifier",
                        cancellationToken =>
                        {
                            LocalDeclarationStatementSyntax newNode = localDeclarationStatement.RemoveModifier(SyntaxKind.ConstKeyword);

                            return(context.Document.ReplaceNodeAsync(localDeclarationStatement, newNode, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ITypeSymbol typeSymbol = semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType;

                    if (typeSymbol?.SupportsExplicitDeclaration() == true)
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Replace 'null' with default value",
                            cancellationToken =>
                            {
                                ExpressionSyntax newNode = typeSymbol.ToDefaultValueSyntax(semanticModel, expression.SpanStart);

                                return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken));
                            },
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse))
                    {
                        break;
                    }

                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    if (!NullCheckExpression.TryCreate(expression, semanticModel, out NullCheckExpression nullCheck, context.CancellationToken))
                    {
                        break;
                    }

                    if (nullCheck.Kind != NullCheckKind.EqualsToNull &&
                        nullCheck.Kind != NullCheckKind.NotEqualsToNull)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove condition",
                        cancellationToken =>
                        {
                            SyntaxNode newRoot = RemoveHelper.RemoveCondition(root, expression, nullCheck.Kind == NullCheckKind.NotEqualsToNull);

                            return(Task.FromResult(context.Document.WithSyntaxRoot(newRoot)));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);

                    break;
                }
                }
            }
        }