コード例 #1
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsAnyCodeFixEnabled(
                    CodeFixIdentifiers.AddOutModifierToArgument,
                    CodeFixIdentifiers.RemoveRefModifier,
                    CodeFixIdentifiers.CreateSingletonArray,
                    CodeFixIdentifiers.AddArgumentList,
                    CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
            {
                return;
            }

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

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out ArgumentSyntax argument))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.ArgumentMustBePassedWithRefOrOutKeyword:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddOutModifierToArgument))
                    {
                        return;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Add 'out' modifier",
                        cancellationToken =>
                        {
                            ArgumentSyntax newArgument = argument
                                                         .WithRefOrOutKeyword(CSharpFactory.OutKeyword())
                                                         .WithFormatterAnnotation();

                            return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);

                    break;
                }

                case CompilerDiagnosticIdentifiers.ArgumentShouldNotBePassedWithRefOrOutKeyword:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveRefModifier))
                    {
                        return;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove 'ref' modifier",
                        cancellationToken =>
                        {
                            ArgumentSyntax newArgument = argument
                                                         .WithRefOrOutKeyword(default(SyntaxToken))
                                                         .PrependToLeadingTrivia(argument.RefOrOutKeyword.GetAllTrivia())
                                                         .WithFormatterAnnotation();

                            return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

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

                case CompilerDiagnosticIdentifiers.CannotConvertArgumentType:
                {
                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression.Kind() == SyntaxKind.NullLiteralExpression &&
                            argument.Parent is ArgumentListSyntax argumentList)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken);

                            if (!parameterSymbols.IsDefault)
                            {
                                int index = argumentList.Arguments.IndexOf(argument);

                                IParameterSymbol parameterSymbol = parameterSymbols[index];

                                ITypeSymbol typeSymbol = parameterSymbol.Type;

                                if (typeSymbol.IsValueType)
                                {
                                    CodeFixRegistrator.ReplaceNullWithDefaultValue(
                                        context,
                                        diagnostic,
                                        expression,
                                        typeSymbol,
                                        semanticModel,
                                        CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue);
                                }
                            }
                        }
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression.IsKind(
                                SyntaxKind.IdentifierName,
                                SyntaxKind.GenericName,
                                SyntaxKind.SimpleMemberAccessExpression))
                        {
                            InvocationExpressionSyntax invocationExpression = InvocationExpression(
                                expression.WithoutTrailingTrivia(),
                                ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia()));

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

                            if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Add argument list",
                                    cancellationToken =>
                                    {
                                        ArgumentSyntax newNode = argument.WithExpression(invocationExpression);

                                        return(context.Document.ReplaceNodeAsync(argument, newNode, cancellationToken));
                                    },
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList));

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

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression?.IsMissing == false)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression);

                            if (typeSymbol?.IsErrorType() == false)
                            {
                                foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken))
                                {
                                    if (!typeSymbol.Equals(typeSymbol2) &&
                                        typeSymbol2 is IArrayTypeSymbol arrayType &&
                                        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;
                                    }
                                }
                            }
                        }
                    }

                    break;
                }
                }
            }
        }
コード例 #2
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            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 is INamedTypeSymbol namedType) &&
                        namedType.IsNullableType())
                    {
                        if (convertedType?.SpecialType == SpecialType.System_Boolean ||
                            AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression))
                        {
                            if (Settings.IsEnabled(diagnostic.Id, 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 (SymbolEqualityComparer.Default.Equals(namedType.TypeArguments[0], convertedType))
                        {
                            if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseCoalesceExpression))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Use coalesce expression",
                                    cancellationToken =>
                                    {
                                        ExpressionSyntax defaultValue = convertedType.GetDefaultValueSyntax(context.Document.GetDefaultSyntaxOptions());

                                        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.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement))
                    {
                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddCastExpression))
                    {
                        CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel);
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer))
                    {
                        ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray) &&
                        type?.IsErrorType() == false &&
                        !SymbolEqualityComparer.Default.Equals(type, convertedType) &&
                        (convertedType is IArrayTypeSymbol arrayType) &&
                        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.IsEnabled(diagnostic.Id, 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:
                {
                    SyntaxNode parent = expression.Parent;

                    if (parent?.IsKind(SyntaxKind.EqualsValueClause) != true)
                    {
                        break;
                    }

                    parent = parent.Parent;

                    if (parent?.IsKind(SyntaxKind.VariableDeclarator) != true)
                    {
                        break;
                    }

                    parent = parent.Parent;

                    if (!(parent is VariableDeclarationSyntax variableDeclaration))
                    {
                        break;
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier) &&
                        variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement)
                    {
                        SyntaxTokenList modifiers = localDeclarationStatement.Modifiers;

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

                        ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword);
                    }
                    else if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConstantWithField) &&
                             variableDeclaration.Variables.Count == 1 &&
                             (variableDeclaration.Parent is FieldDeclarationSyntax fieldDeclaration) &&
                             fieldDeclaration.Modifiers.Contains(SyntaxKind.ConstKeyword))
                    {
                        CodeAction codeAction = CodeAction.Create(
                            ReplaceConstantWithFieldRefactoring.Title,
                            cancellationToken => ReplaceConstantWithFieldRefactoring.RefactorAsync(context.Document, fieldDeclaration, cancellationToken),
                            GetEquivalenceKey(diagnostic));

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

                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType:
                case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeParameterBecauseItCouldBeNonNullableValueType:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
                    {
                        break;
                    }

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

                    CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse))
                    {
                        break;
                    }

                    NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedStyles: NullCheckStyles.ComparisonToNull);

                    if (!nullCheck.Success)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove condition",
                        cancellationToken =>
                        {
                            cancellationToken.ThrowIfCancellationRequested();

                            SyntaxNode newRoot = RemoveCondition(root, expression, nullCheck.Style == NullCheckStyles.NotEqualsToNull);

                            cancellationToken.ThrowIfCancellationRequested();

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

                    context.RegisterCodeFix(codeAction, diagnostic);

                    break;
                }

                case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement:
                {
                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveParentheses) &&
                        expression is ParenthesizedExpressionSyntax parenthesizedExpression &&
                        parenthesizedExpression?.IsMissing == false)
                    {
                        CodeAction codeAction = CodeActionFactory.RemoveParentheses(context.Document, parenthesizedExpression, equivalenceKey: GetEquivalenceKey(diagnostic));

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

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

                    if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause)
                    {
                        if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression))
                        {
                            break;
                        }

                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                    }
                    else if (expression.Parent is ExpressionStatementSyntax expressionStatement)
                    {
                        if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList) &&
                            expression.IsKind(
                                SyntaxKind.IdentifierName,
                                SyntaxKind.SimpleMemberAccessExpression))
                        {
                            SyntaxNode invocationExpression = InvocationExpression(expression);

                            if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Add argument list",
                                    cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }

                        if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceComparisonWithAssignment) &&
                            expression.IsKind(SyntaxKind.EqualsExpression))
                        {
                            BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(expression);

                            if (!info.Success)
                            {
                                break;
                            }

                            ITypeSymbol leftTypeSymbol = semanticModel.GetTypeSymbol(info.Left, context.CancellationToken);

                            if (leftTypeSymbol?.IsErrorType() != false)
                            {
                                break;
                            }

                            if (!semanticModel.IsImplicitConversion(info.Right, leftTypeSymbol))
                            {
                                break;
                            }

                            CodeAction codeAction = CodeAction.Create(
                                "Replace comparison with assignment",
                                cancellationToken =>
                                {
                                    AssignmentExpressionSyntax simpleAssignment = SimpleAssignmentExpression(info.Left, info.Right).WithTriviaFrom(expression);
                                    return(context.Document.ReplaceNodeAsync(expression, simpleAssignment, cancellationToken));
                                },
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceComparisonWithAssignment));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }

                        if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse) &&
                            (expression is ConditionalExpressionSyntax conditionalExpression) &&
                            conditionalExpression.Condition != null)
                        {
                            ExpressionSyntax whenTrue  = conditionalExpression.WhenTrue;
                            ExpressionSyntax whenFalse = conditionalExpression.WhenFalse;

                            if (whenTrue != null &&
                                whenFalse != null &&
                                semanticModel.GetTypeSymbol(whenTrue, context.CancellationToken)?.SpecialType == SpecialType.System_Void &&
                                semanticModel.GetTypeSymbol(whenFalse, context.CancellationToken)?.SpecialType == SpecialType.System_Void)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Replace ?: with if-else",
                                    cancellationToken =>
                                    {
                                        IfStatementSyntax newNode = IfStatement(
                                            conditionalExpression.Condition.WalkDownParentheses(),
                                            Block(ExpressionStatement(whenTrue)),
                                            ElseClause(Block(ExpressionStatement(whenFalse))));

                                        newNode = newNode
                                                  .WithTriviaFrom(expressionStatement)
                                                  .WithFormatterAnnotation();

                                        return(context.Document.ReplaceNodeAsync(expressionStatement, newNode, cancellationToken));
                                    },
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }

                        if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false)
                        {
                            break;
                        }

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

                        if (typeSymbol?.IsErrorType() != false)
                        {
                            break;
                        }

                        if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceLocalVariable) &&
                            !expressionStatement.IsEmbedded())
                        {
                            bool addAwait = typeSymbol.OriginalDefinition.EqualsOrInheritsFromTaskOfT() &&
                                            semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod();

                            CodeAction codeAction = CodeAction.Create(
                                IntroduceLocalVariableRefactoring.GetTitle(expression),
                                cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }

                        if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceField))
                        {
                            CodeAction codeAction = CodeAction.Create(
                                $"Introduce field for '{expression}'",
                                cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType:
                {
                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceYieldReturnWithForEach) &&
                        expression.IsParentKind(SyntaxKind.YieldReturnStatement))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ReplaceYieldReturnWithForEachRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                        break;
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement, SyntaxKind.ArrowExpressionClause))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                        break;
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        CodeFixRegistrationResult result = ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);

                        if (!result.Success)
                        {
                            RemoveAssignmentOfVoidExpression(context, diagnostic, expression, semanticModel);
                        }

                        break;
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) &&
                        expression?.Kind() == SyntaxKind.StringLiteralExpression)
                    {
                        var literalExpression = (LiteralExpressionSyntax)expression;

                        if (literalExpression.Token.ValueText.Length == 1)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.SpecialType == SpecialType.System_Char)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Replace string literal with character literal",
                                    cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral));

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

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement))
                    {
                        var returnStatement = (ReturnStatementSyntax)expression.Parent;

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

                        ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken);

                        if (containingSymbol?.Kind == SymbolKind.Method &&
                            ((IMethodSymbol)containingSymbol).ReturnType.OriginalDefinition.IsIEnumerableOrIEnumerableOfT())
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Use yield return instead of return",
                                cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.LeftHandSideOfAssignmentMustBeVariablePropertyOrIndexer:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier))
                    {
                        return;
                    }

                    if (!expression.IsKind(SyntaxKind.IdentifierName))
                    {
                        return;
                    }

                    if (!expression.IsParentKind(SyntaxKind.SimpleAssignmentExpression))
                    {
                        return;
                    }

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

                    SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken);

                    if (symbolInfo.CandidateReason != CandidateReason.NotAVariable)
                    {
                        return;
                    }

                    if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is ILocalSymbol localSymbol))
                    {
                        return;
                    }

                    if (!localSymbol.IsConst)
                    {
                        return;
                    }

                    SyntaxNode node = localSymbol.GetSyntaxOrDefault(context.CancellationToken);

                    if (!node.IsKind(SyntaxKind.VariableDeclarator))
                    {
                        return;
                    }

                    node = node.Parent;

                    if (!node.IsKind(SyntaxKind.VariableDeclaration))
                    {
                        return;
                    }

                    node = node.Parent;

                    if (!(node is LocalDeclarationStatementSyntax localDeclaration))
                    {
                        return;
                    }

                    SyntaxToken constModifier = localDeclaration.Modifiers.Find(SyntaxKind.ConstKeyword);

                    if (!constModifier.IsKind(SyntaxKind.ConstKeyword))
                    {
                        return;
                    }

                    ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclaration, constModifier);

                    break;
                }

                case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBeAssignedTo:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable))
                    {
                        break;
                    }

                    SimpleAssignmentExpressionInfo simpleAssignment = SyntaxInfo.SimpleAssignmentExpressionInfo(expression.Parent);

                    if (!simpleAssignment.Success)
                    {
                        return;
                    }

                    if (simpleAssignment.Left != expression)
                    {
                        return;
                    }

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

                    SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken);

                    if (symbolInfo.CandidateReason != CandidateReason.NotAVariable)
                    {
                        return;
                    }

                    if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol))
                    {
                        return;
                    }

                    if (fieldSymbol.DeclaredAccessibility != Accessibility.Private)
                    {
                        return;
                    }

                    if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration))
                    {
                        return;
                    }

                    TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>();

                    if (!expression.Ancestors().Any(f => f == containingTypeDeclaration))
                    {
                        return;
                    }

                    ModifiersCodeFixRegistrator.RemoveModifier(
                        context,
                        diagnostic,
                        fieldDeclaration,
                        SyntaxKind.ReadOnlyKeyword,
                        title: $"Make '{fieldSymbol.Name}' writable");

                    break;
                }
                }
            }
        }
コード例 #3
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveSemicolon) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeForEachType))
            {
                return;
            }

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

            if (!TryFindToken(root, context.Span.Start, out SyntaxToken token))
            {
                return;
            }

            SyntaxKind kind = token.Kind();

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.OperatorCannotBeAppliedToOperandOfType:
                {
                    if (kind == SyntaxKind.QuestionToken &&
                        token.Parent is ConditionalAccessExpressionSyntax conditionalAccess)
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken);

                        if (typeSymbol?.IsErrorType() == false &&
                            !typeSymbol.IsConstructedFrom(SpecialType.System_Nullable_T))
                        {
                            if (typeSymbol.IsValueType)
                            {
                                if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess))
                                {
                                    CodeAction codeAction = CodeAction.Create(
                                        "Remove '?' operator",
                                        cancellationToken =>
                                        {
                                            var textChange = new TextChange(token.Span, "");
                                            return(context.Document.WithTextChangeAsync(textChange, cancellationToken));
                                        },
                                        GetEquivalenceKey(diagnostic));

                                    context.RegisterCodeFix(codeAction, diagnostic);
                                }
                            }
                            else if (typeSymbol.IsReferenceType)
                            {
                                if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) &&
                                    conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax memberBindingExpression)
                                {
                                    ConditionalAccessExpressionSyntax newNode = conditionalAccess.WithWhenNotNull(
                                        InvocationExpression(
                                            memberBindingExpression.WithoutTrailingTrivia(),
                                            ArgumentList().WithTrailingTrivia(memberBindingExpression.GetTrailingTrivia())));

                                    CodeAction codeAction = CodeAction.Create(
                                        "Add argument list",
                                        cancellationToken => context.Document.ReplaceNodeAsync(conditionalAccess, newNode, cancellationToken),
                                        GetEquivalenceKey(diagnostic));

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

                        if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList))
                        {
                            break;
                        }

                        if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveConditionalAccess))
                        {
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers))
                    {
                        break;
                    }

                    ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token);
                    break;
                }

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

                    if (!(token.Parent is ParameterSyntax parameter))
                    {
                        break;
                    }

                    ExpressionSyntax value = parameter.Default?.Value;

                    if (value == null)
                    {
                        break;
                    }

                    if (value.Kind() != SyntaxKind.NullLiteralExpression)
                    {
                        break;
                    }

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

                    CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ObjectOfTypeConvertibleToTypeIsRequired:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.ReturnKeyword)
                    {
                        break;
                    }

                    if (!token.IsParentKind(SyntaxKind.ReturnStatement))
                    {
                        break;
                    }

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

                    ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken);

                    if (symbol == null)
                    {
                        break;
                    }

                    SymbolKind symbolKind = symbol.Kind;

                    ITypeSymbol typeSymbol = null;

                    if (symbolKind == SymbolKind.Method)
                    {
                        var methodSymbol = (IMethodSymbol)symbol;

                        typeSymbol = methodSymbol.ReturnType;

                        if (methodSymbol.IsAsync &&
                            (typeSymbol is INamedTypeSymbol namedTypeSymbol))
                        {
                            ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments;

                            if (typeArguments.Any())
                            {
                                typeSymbol = typeArguments[0];
                            }
                        }
                    }
                    else if (symbolKind == SymbolKind.Property)
                    {
                        typeSymbol = ((IPropertySymbol)symbol).Type;
                    }
                    else
                    {
                        Debug.Fail(symbolKind.ToString());
                    }

                    if (typeSymbol == null)
                    {
                        break;
                    }

                    if (typeSymbol.Kind == SymbolKind.ErrorType)
                    {
                        break;
                    }

                    if (!typeSymbol.SupportsExplicitDeclaration())
                    {
                        break;
                    }

                    var returnStatement = (ReturnStatementSyntax)token.Parent;

                    CodeAction codeAction = CodeAction.Create(
                        "Return default value",
                        cancellationToken =>
                        {
                            ExpressionSyntax expression = typeSymbol.ToDefaultValueSyntax(semanticModel, returnStatement.SpanStart);

                            ReturnStatementSyntax newNode = returnStatement.WithExpression(expression);

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

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

                case CompilerDiagnosticIdentifiers.TypeExpected:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.CloseParenToken)
                    {
                        break;
                    }

                    if (!(token.Parent is DefaultExpressionSyntax defaultExpression))
                    {
                        break;
                    }

                    if (!(defaultExpression.Type is IdentifierNameSyntax identifierName))
                    {
                        break;
                    }

                    if (!identifierName.IsMissing)
                    {
                        break;
                    }

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

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

                    ITypeSymbol convertedType = typeInfo.ConvertedType;

                    if (convertedType?.SupportsExplicitDeclaration() != true)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        $"Add type '{SymbolDisplay.GetMinimalString(convertedType, semanticModel, defaultExpression.SpanStart)}'",
                        cancellationToken =>
                        {
                            TypeSyntax newNode = convertedType.ToMinimalTypeSyntax(semanticModel, defaultExpression.SpanStart);

                            newNode = newNode
                                      .WithTriviaFrom(identifierName)
                                      .WithFormatterAnnotation();

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

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

                case CompilerDiagnosticIdentifiers.SemicolonAfterMethodOrAccessorBlockIsNotValid:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.RemoveSemicolon))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.SemicolonToken)
                    {
                        break;
                    }

                    switch (token.Parent)
                    {
                    case MethodDeclarationSyntax methodDeclaration:
                    {
                        BlockSyntax body = methodDeclaration.Body;

                        if (body == null)
                        {
                            break;
                        }

                        CodeAction codeAction = CodeAction.Create(
                            "Remove semicolon",
                            cancellationToken =>
                                {
                                    SyntaxTriviaList trivia = body
                                                              .GetTrailingTrivia()
                                                              .EmptyIfWhitespace()
                                                              .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                              .AddRange(token.TrailingTrivia);

                                    MethodDeclarationSyntax newNode = methodDeclaration
                                                                      .WithBody(body.WithTrailingTrivia(trivia))
                                                                      .WithSemicolonToken(default(SyntaxToken));

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

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

                    case PropertyDeclarationSyntax propertyDeclaration:
                    {
                        AccessorListSyntax accessorList = propertyDeclaration.AccessorList;

                        if (accessorList == null)
                        {
                            break;
                        }

                        CodeAction codeAction = CodeAction.Create(
                            "Remove semicolon",
                            cancellationToken =>
                                {
                                    SyntaxTriviaList trivia = accessorList
                                                              .GetTrailingTrivia()
                                                              .EmptyIfWhitespace()
                                                              .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                              .AddRange(token.TrailingTrivia);

                                    PropertyDeclarationSyntax newNode = propertyDeclaration
                                                                        .WithAccessorList(accessorList.WithTrailingTrivia(trivia))
                                                                        .WithSemicolonToken(default(SyntaxToken));

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

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

                    case AccessorDeclarationSyntax accessorDeclaration:
                    {
                        BlockSyntax body = accessorDeclaration.Body;

                        if (body == null)
                        {
                            break;
                        }

                        CodeAction codeAction = CodeAction.Create(
                            "Remove semicolon",
                            cancellationToken =>
                                {
                                    SyntaxTriviaList trivia = body
                                                              .GetTrailingTrivia()
                                                              .EmptyIfWhitespace()
                                                              .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                              .AddRange(token.TrailingTrivia);

                                    AccessorDeclarationSyntax newNode = accessorDeclaration
                                                                        .WithBody(body.WithTrailingTrivia(trivia))
                                                                        .WithSemicolonToken(default(SyntaxToken));

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

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

                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotConvertType:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeForEachType))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.ForEachKeyword)
                    {
                        break;
                    }

                    if (!(token.Parent is ForEachStatementSyntax forEachStatement))
                    {
                        break;
                    }

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

                    ForEachStatementInfo info = semanticModel.GetForEachStatementInfo(forEachStatement);

                    ITypeSymbol typeSymbol = info.ElementType;

                    if (typeSymbol.SupportsExplicitDeclaration())
                    {
                        CodeFixRegistrator.ChangeType(context, diagnostic, forEachStatement.Type, typeSymbol, semanticModel, CodeFixIdentifiers.ChangeForEachType);
                    }

                    CodeFixRegistrator.ChangeTypeToVar(context, diagnostic, forEachStatement.Type, CodeFixIdentifiers.ChangeTypeToVar);
                    break;
                }
                }
            }
        }
コード例 #4
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out ArgumentSyntax argument))
            {
                return;
            }

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.ArgumentMustBePassedWithRefOrOutKeyword:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddOutModifierToArgument))
                    {
                        return;
                    }

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

                    IParameterSymbol parameter = semanticModel.DetermineParameter(argument, allowCandidate: true, cancellationToken: context.CancellationToken);

                    if (parameter == null)
                    {
                        return;
                    }

                    SyntaxToken refOrOutKeyword = default;

                    if (parameter.RefKind == RefKind.Out)
                    {
                        refOrOutKeyword = Token(SyntaxKind.OutKeyword);
                    }
                    else if (parameter.RefKind == RefKind.Ref)
                    {
                        refOrOutKeyword = Token(SyntaxKind.RefKeyword);
                    }
                    else
                    {
                        return;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        $"Add '{SyntaxFacts.GetText(refOrOutKeyword.Kind())}' modifier",
                        cancellationToken =>
                        {
                            ArgumentSyntax newArgument = argument
                                                         .WithRefOrOutKeyword(refOrOutKeyword)
                                                         .WithFormatterAnnotation();

                            return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

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

                case CompilerDiagnosticIdentifiers.ArgumentShouldNotBePassedWithRefOrOutKeyword:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveRefModifier))
                    {
                        return;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove 'ref' modifier",
                        cancellationToken =>
                        {
                            ArgumentSyntax newArgument = argument
                                                         .WithRefOrOutKeyword(default(SyntaxToken))
                                                         .PrependToLeadingTrivia(argument.RefOrOutKeyword.GetAllTrivia())
                                                         .WithFormatterAnnotation();

                            return(context.Document.ReplaceNodeAsync(argument, newArgument, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

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

                case CompilerDiagnosticIdentifiers.CannotConvertArgumentType:
                {
                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression.Kind() == SyntaxKind.NullLiteralExpression &&
                            argument.Parent is ArgumentListSyntax argumentList)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken);

                            if (!parameterSymbols.IsDefault)
                            {
                                int index = argumentList.Arguments.IndexOf(argument);

                                IParameterSymbol parameterSymbol = parameterSymbols[index];

                                ITypeSymbol typeSymbol = parameterSymbol.Type;

                                if (typeSymbol.IsValueType)
                                {
                                    CodeFixRegistrator.ReplaceNullWithDefaultValue(
                                        context,
                                        diagnostic,
                                        expression,
                                        typeSymbol,
                                        CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue);
                                }
                            }
                        }
                    }

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression.IsKind(
                                SyntaxKind.IdentifierName,
                                SyntaxKind.GenericName,
                                SyntaxKind.SimpleMemberAccessExpression))
                        {
                            InvocationExpressionSyntax invocationExpression = InvocationExpression(
                                expression.WithoutTrailingTrivia(),
                                ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia()));

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

                            if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Add argument list",
                                    cancellationToken =>
                                    {
                                        ArgumentSyntax newNode = argument.WithExpression(invocationExpression);

                                        return(context.Document.ReplaceNodeAsync(argument, newNode, cancellationToken));
                                    },
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList));

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

                    if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray))
                    {
                        ExpressionSyntax expression = argument.Expression;

                        if (expression?.IsMissing == false)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression);

                            if (typeSymbol?.IsErrorType() == false)
                            {
                                foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken))
                                {
                                    if (!typeSymbol.Equals(typeSymbol2) &&
                                        typeSymbol2 is IArrayTypeSymbol arrayType &&
                                        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;
                                    }
                                }
                            }
                        }
                    }

                    break;
                }

                case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBePassedAsRefOrOutValue:
                {
                    if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable))
                    {
                        return;
                    }

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

                    SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(argument.Expression, context.CancellationToken);

                    if (symbolInfo.CandidateReason != CandidateReason.NotAVariable)
                    {
                        return;
                    }

                    if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol))
                    {
                        return;
                    }

                    if (fieldSymbol.DeclaredAccessibility != Accessibility.Private)
                    {
                        return;
                    }

                    if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration))
                    {
                        return;
                    }

                    TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>();

                    if (!argument.Ancestors().Any(f => f == containingTypeDeclaration))
                    {
                        return;
                    }

                    ModifiersCodeFixRegistrator.RemoveModifier(
                        context,
                        diagnostic,
                        fieldDeclaration,
                        SyntaxKind.ReadOnlyKeyword,
                        title: $"Make '{fieldSymbol.Name}' writable",
                        additionalKey: CodeFixIdentifiers.MakeFieldWritable);

                    break;
                }
                }
            }
        }
コード例 #5
0
        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            if (!Settings.IsAnyCodeFixEnabled(
                    CodeFixIdentifiers.AddArgumentList,
                    CodeFixIdentifiers.ReorderModifiers,
                    CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue,
                    CodeFixIdentifiers.ReturnDefaultValue,
                    CodeFixIdentifiers.AddMissingType))
            {
                return;
            }

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

            if (!TryFindToken(root, context.Span.Start, out SyntaxToken token))
            {
                return;
            }

            SyntaxKind kind = token.Kind();

            foreach (Diagnostic diagnostic in context.Diagnostics)
            {
                switch (diagnostic.Id)
                {
                case CompilerDiagnosticIdentifiers.OperatorCannotBeAppliedToOperandOfType:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList))
                    {
                        break;
                    }

                    if (kind != SyntaxKind.QuestionToken)
                    {
                        break;
                    }

                    if (!token.IsParentKind(SyntaxKind.ConditionalAccessExpression))
                    {
                        break;
                    }

                    var conditionalAccess = (ConditionalAccessExpressionSyntax)token.Parent;

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

                    ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken);

                    if (typeSymbol == null ||
                        typeSymbol.IsErrorType() ||
                        !typeSymbol.IsValueType ||
                        typeSymbol.IsConstructedFrom(SpecialType.System_Nullable_T))
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove '?' operator",
                        cancellationToken =>
                        {
                            var textChange = new TextChange(token.Span, "");
                            return(context.Document.WithTextChangeAsync(textChange, cancellationToken));
                        },
                        GetEquivalenceKey(diagnostic));

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

                case CompilerDiagnosticIdentifiers.PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReorderModifiers))
                    {
                        break;
                    }

                    ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token);
                    break;
                }

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

                    if (!(token.Parent is ParameterSyntax parameter))
                    {
                        break;
                    }

                    ExpressionSyntax value = parameter.Default?.Value;

                    if (value == null)
                    {
                        break;
                    }

                    if (value.Kind() != SyntaxKind.NullLiteralExpression)
                    {
                        break;
                    }

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

                    CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel);
                    break;
                }

                case CompilerDiagnosticIdentifiers.ObjectOfTypeConvertibleToTypeIsRequired:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReturnDefaultValue))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.ReturnKeyword)
                    {
                        break;
                    }

                    if (!token.IsParentKind(SyntaxKind.ReturnStatement))
                    {
                        break;
                    }

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

                    ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken);

                    if (symbol == null)
                    {
                        break;
                    }

                    SymbolKind symbolKind = symbol.Kind;

                    ITypeSymbol typeSymbol = null;

                    if (symbolKind == SymbolKind.Method)
                    {
                        var methodSymbol = (IMethodSymbol)symbol;

                        typeSymbol = methodSymbol.ReturnType;

                        if (methodSymbol.IsAsync &&
                            (typeSymbol is INamedTypeSymbol namedTypeSymbol))
                        {
                            ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments;

                            if (typeArguments.Any())
                            {
                                typeSymbol = typeArguments[0];
                            }
                        }
                    }
                    else if (symbolKind == SymbolKind.Property)
                    {
                        typeSymbol = ((IPropertySymbol)symbol).Type;
                    }
                    else
                    {
                        Debug.Fail(symbolKind.ToString());
                    }

                    if (typeSymbol == null)
                    {
                        break;
                    }

                    if (typeSymbol.Kind == SymbolKind.ErrorType)
                    {
                        break;
                    }

                    if (!typeSymbol.SupportsExplicitDeclaration())
                    {
                        break;
                    }

                    var returnStatement = (ReturnStatementSyntax)token.Parent;

                    CodeAction codeAction = CodeAction.Create(
                        "Return default value",
                        cancellationToken =>
                        {
                            ExpressionSyntax expression = typeSymbol.ToDefaultValueSyntax(semanticModel, returnStatement.SpanStart);

                            ReturnStatementSyntax newNode = returnStatement.WithExpression(expression);

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

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

                case CompilerDiagnosticIdentifiers.TypeExpected:
                {
                    if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddMissingType))
                    {
                        break;
                    }

                    if (token.Kind() != SyntaxKind.CloseParenToken)
                    {
                        break;
                    }

                    if (!(token.Parent is DefaultExpressionSyntax defaultExpression))
                    {
                        break;
                    }

                    if (!(defaultExpression.Type is IdentifierNameSyntax identifierName))
                    {
                        break;
                    }

                    if (!identifierName.IsMissing)
                    {
                        break;
                    }

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

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

                    ITypeSymbol convertedType = typeInfo.ConvertedType;

                    if (convertedType?.SupportsExplicitDeclaration() != true)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        $"Add type '{SymbolDisplay.GetMinimalString(convertedType, semanticModel, defaultExpression.SpanStart)}'",
                        cancellationToken =>
                        {
                            TypeSyntax newNode = convertedType.ToMinimalTypeSyntax(semanticModel, defaultExpression.SpanStart);

                            newNode = newNode
                                      .WithTriviaFrom(identifierName)
                                      .WithFormatterAnnotation();

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

                    context.RegisterCodeFix(codeAction, diagnostic);
                    break;
                }
                }
            }
        }
コード例 #6
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindFirstAncestorOrSelf(root, context.Span, out ExpressionSyntax expression, getInnermostNodeForTie: false))
            {
                return;
            }

            if (expression.Parent is not ArgumentSyntax argument ||
                argument.Expression != expression)
            {
                return;
            }

            Document   document   = context.Document;
            Diagnostic diagnostic = context.Diagnostics[0];

            switch (diagnostic.Id)
            {
            case CompilerDiagnosticIdentifiers.CS1503_CannotConvertArgumentType:
            {
                if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue, document, root.SyntaxTree) &&
                    expression.Kind() == SyntaxKind.NullLiteralExpression &&
                    argument.Parent is ArgumentListSyntax argumentList)
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ImmutableArray <IParameterSymbol> parameterSymbols = FindParameters(argumentList, semanticModel, context.CancellationToken);

                    if (!parameterSymbols.IsDefault)
                    {
                        int index = argumentList.Arguments.IndexOf(argument);

                        IParameterSymbol parameterSymbol = parameterSymbols[index];

                        ITypeSymbol typeSymbol = parameterSymbol.Type;

                        if (typeSymbol.IsValueType)
                        {
                            CodeFixRegistrator.ReplaceNullWithDefaultValue(
                                context,
                                diagnostic,
                                expression,
                                typeSymbol,
                                CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue);
                        }
                    }
                }

                if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList, document, root.SyntaxTree) &&
                    expression.IsKind(
                        SyntaxKind.IdentifierName,
                        SyntaxKind.GenericName,
                        SyntaxKind.SimpleMemberAccessExpression))
                {
                    InvocationExpressionSyntax invocationExpression = InvocationExpression(
                        expression.WithoutTrailingTrivia(),
                        ArgumentList().WithTrailingTrivia(expression.GetTrailingTrivia()));

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

                    if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null)
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Add argument list",
                            ct =>
                            {
                                ArgumentSyntax newNode = argument.WithExpression(invocationExpression);

                                return(document.ReplaceNodeAsync(argument, newNode, ct));
                            },
                            GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList));

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

                if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray, document, root.SyntaxTree) &&
                    expression?.IsMissing == false)
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression);

                    if (typeSymbol?.IsErrorType() == false)
                    {
                        foreach (ITypeSymbol typeSymbol2 in DetermineParameterTypeHelper.DetermineParameterTypes(argument, semanticModel, context.CancellationToken))
                        {
                            if (!SymbolEqualityComparer.Default.Equals(typeSymbol, typeSymbol2) &&
                                typeSymbol2 is IArrayTypeSymbol arrayType &&
                                semanticModel.IsImplicitConversion(expression, arrayType.ElementType))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Create singleton array",
                                    ct => CreateSingletonArrayRefactoring.RefactorAsync(document, expression, arrayType.ElementType, semanticModel, ct),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray));

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

                break;
            }
            }
        }
コード例 #7
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) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceLocalVariable) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceField) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) &&
                !Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList))
            {
                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 is INamedTypeSymbol namedType) &&
                        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.ChangeMemberTypeAccordingToReturnExpression) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement))
                    {
                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddCastExpression))
                    {
                        CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel);
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.CreateSingletonArray) &&
                        type?.IsErrorType() == false &&
                        !type.Equals(convertedType) &&
                        (convertedType is IArrayTypeSymbol arrayType) &&
                        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;
                    }

                    ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword);

                    break;
                }

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

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

                    CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel);
                    break;
                }

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

                    NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedKinds: NullCheckKind.ComparisonToNull);

                    if (!nullCheck.Success)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove condition",
                        cancellationToken =>
                        {
                            cancellationToken.ThrowIfCancellationRequested();

                            SyntaxNode newRoot = RemoveHelper.RemoveCondition(root, expression, nullCheck.Kind == NullCheckKind.NotEqualsToNull);

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

                    context.RegisterCodeFix(codeAction, diagnostic);

                    break;
                }

                case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement:
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause)
                    {
                        if (!Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression))
                        {
                            break;
                        }

                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                    }
                    else if (expression.Parent is ExpressionStatementSyntax expressionStatement)
                    {
                        if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.AddArgumentList) &&
                            expression.IsKind(
                                SyntaxKind.IdentifierName,
                                SyntaxKind.SimpleMemberAccessExpression))
                        {
                            SyntaxNode invocationExpression = SyntaxFactory.InvocationExpression(expression);

                            if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Add argument list",
                                    cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }

                        if (Settings.IsAnyCodeFixEnabled(
                                CodeFixIdentifiers.IntroduceLocalVariable,
                                CodeFixIdentifiers.IntroduceField))
                        {
                            if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false)
                            {
                                break;
                            }

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

                            if (typeSymbol?.IsErrorType() != false)
                            {
                                break;
                            }

                            if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceLocalVariable) &&
                                !expressionStatement.IsEmbedded())
                            {
                                bool addAwait = typeSymbol.IsConstructedFromTaskOfT(semanticModel) &&
                                                semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod();

                                CodeAction codeAction = CodeAction.Create(
                                    IntroduceLocalVariableRefactoring.GetTitle(expression),
                                    cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }

                            if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.IntroduceField))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    $"Introduce field for '{expression}'",
                                    cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField));

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

                    break;
                }

                case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType:
                {
                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel);
                        break;
                    }

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) &&
                        expression?.IsKind(SyntaxKind.StringLiteralExpression) == true)
                    {
                        var literalExpression = (LiteralExpressionSyntax)expression;

                        if (literalExpression.Token.ValueText.Length == 1)
                        {
                            SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                            if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.IsChar() == true)
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Replace string literal with character literal",
                                    cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken),
                                    GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral));

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

                    if (Settings.IsCodeFixEnabled(CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) &&
                        expression.IsParentKind(SyntaxKind.ReturnStatement))
                    {
                        var returnStatement = (ReturnStatementSyntax)expression.Parent;

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

                        ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken);

                        if (containingSymbol?.IsKind(SymbolKind.Method) == true &&
                            ((IMethodSymbol)containingSymbol).ReturnType?.IsIEnumerableOrConstructedFromIEnumerableOfT() == true)
                        {
                            CodeAction codeAction = CodeAction.Create(
                                "Use yield return instead of return",
                                cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken),
                                GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn));

                            context.RegisterCodeFix(codeAction, diagnostic);
                        }
                    }

                    break;
                }
                }
            }
        }
コード例 #8
0
        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false);

            if (!TryFindToken(root, context.Span.Start, out SyntaxToken token))
            {
                return;
            }

            SyntaxKind kind       = token.Kind();
            Diagnostic diagnostic = context.Diagnostics[0];
            Document   document   = context.Document;

            switch (diagnostic.Id)
            {
            case CompilerDiagnosticIdentifiers.CS0023_OperatorCannotBeAppliedToOperand:
            {
                if (kind == SyntaxKind.QuestionToken &&
                    token.Parent is ConditionalAccessExpressionSyntax conditionalAccess)
                {
                    SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                    ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(conditionalAccess.Expression, context.CancellationToken);

                    if (typeSymbol?.IsErrorType() == false &&
                        !typeSymbol.IsNullableType())
                    {
                        if (typeSymbol.IsValueType)
                        {
                            if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionalAccess, document, root.SyntaxTree))
                            {
                                CodeAction codeAction = CodeAction.Create(
                                    "Remove '?' operator",
                                    ct => document.WithTextChangeAsync(token.Span, "", ct),
                                    GetEquivalenceKey(diagnostic));

                                context.RegisterCodeFix(codeAction, diagnostic);
                            }
                        }
                        else if (typeSymbol.IsReferenceType)
                        {
                            if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList, document, root.SyntaxTree) &&
                                conditionalAccess.WhenNotNull is MemberBindingExpressionSyntax memberBindingExpression)
                            {
                                ConditionalAccessExpressionSyntax newNode = conditionalAccess.WithWhenNotNull(
                                    InvocationExpression(
                                        memberBindingExpression.WithoutTrailingTrivia(),
                                        ArgumentList().WithTrailingTrivia(memberBindingExpression.GetTrailingTrivia())));

                                CodeAction codeAction = CodeAction.Create(
                                    "Add argument list",
                                    ct => document.ReplaceNodeAsync(conditionalAccess, newNode, ct),
                                    GetEquivalenceKey(diagnostic));

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

                break;
            }

            case CompilerDiagnosticIdentifiers.CS0267_PartialModifierCanOnlyAppearImmediatelyBeforeClassStructInterfaceOrVoid:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.OrderModifiers, document, root.SyntaxTree))
                {
                    break;
                }

                ModifiersCodeFixRegistrator.MoveModifier(context, diagnostic, token.Parent, token);
                break;
            }

            case CompilerDiagnosticIdentifiers.CS1750_ValueCannotBeUsedAsDefaultParameter:
            {
                if (token.Parent is not ParameterSyntax parameter)
                {
                    break;
                }

                ExpressionSyntax value = parameter.Default?.Value;

                if (value == null)
                {
                    break;
                }

                if (value.IsKind(SyntaxKind.NullLiteralExpression))
                {
                    if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue, document, root.SyntaxTree))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, value, semanticModel);
                    }
                }
                else if (!value.IsKind(SyntaxKind.DefaultExpression, SyntaxKind.DefaultLiteralExpression))
                {
                    if (IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeParameterType, document, root.SyntaxTree))
                    {
                        SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false);

                        ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(value, context.CancellationToken);

                        if (!typeSymbol.IsKind(SymbolKind.ErrorType))
                        {
                            CodeFixRegistrator.ChangeType(context, diagnostic, parameter.Type, typeSymbol, semanticModel);
                        }
                    }
                }

                break;
            }

            case CompilerDiagnosticIdentifiers.CS0126_ObjectOfTypeConvertibleToTypeIsRequired:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReturnDefaultValue, document, root.SyntaxTree))
                {
                    break;
                }

                if (!token.IsKind(SyntaxKind.ReturnKeyword))
                {
                    break;
                }

                if (!token.IsParentKind(SyntaxKind.ReturnStatement))
                {
                    break;
                }

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

                ISymbol symbol = semanticModel.GetEnclosingSymbol(token.SpanStart, context.CancellationToken);

                if (symbol == null)
                {
                    break;
                }

                SymbolKind symbolKind = symbol.Kind;

                ITypeSymbol typeSymbol = null;

                if (symbolKind == SymbolKind.Method)
                {
                    var methodSymbol = (IMethodSymbol)symbol;

                    typeSymbol = methodSymbol.ReturnType;

                    if (methodSymbol.IsAsync &&
                        (typeSymbol is INamedTypeSymbol namedTypeSymbol))
                    {
                        ImmutableArray <ITypeSymbol> typeArguments = namedTypeSymbol.TypeArguments;

                        if (typeArguments.Any())
                        {
                            typeSymbol = typeArguments[0];
                        }
                    }
                }
                else if (symbolKind == SymbolKind.Property)
                {
                    typeSymbol = ((IPropertySymbol)symbol).Type;
                }
                else
                {
                    Debug.Fail(symbolKind.ToString());
                }

                if (typeSymbol == null)
                {
                    break;
                }

                if (typeSymbol.Kind == SymbolKind.ErrorType)
                {
                    break;
                }

                if (!typeSymbol.SupportsExplicitDeclaration())
                {
                    break;
                }

                var returnStatement = (ReturnStatementSyntax)token.Parent;

                CodeAction codeAction = CodeAction.Create(
                    "Return default value",
                    ct =>
                    {
                        ExpressionSyntax expression = typeSymbol.GetDefaultValueSyntax(document.GetDefaultSyntaxOptions());

                        if (expression.IsKind(SyntaxKind.DefaultExpression) &&
                            document.SupportsLanguageFeature(CSharpLanguageFeature.DefaultLiteral))
                        {
                            expression = CSharpFactory.DefaultLiteralExpression().WithTriviaFrom(expression);
                        }

                        ReturnStatementSyntax newNode = returnStatement.WithExpression(expression);

                        return(document.ReplaceNodeAsync(returnStatement, newNode, ct));
                    },
                    GetEquivalenceKey(diagnostic));

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

            case CompilerDiagnosticIdentifiers.CS1031_TypeExpected:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddMissingType, document, root.SyntaxTree))
                {
                    break;
                }

                if (!token.IsKind(SyntaxKind.CloseParenToken))
                {
                    break;
                }

                if (token.Parent is not DefaultExpressionSyntax defaultExpression)
                {
                    break;
                }

                if (defaultExpression.Type is not IdentifierNameSyntax identifierName)
                {
                    break;
                }

                if (!identifierName.IsMissing)
                {
                    break;
                }

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

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

                ITypeSymbol convertedType = typeInfo.ConvertedType;

                if (convertedType?.SupportsExplicitDeclaration() != true)
                {
                    break;
                }

                CodeAction codeAction = CodeAction.Create(
                    $"Add type '{SymbolDisplay.ToMinimalDisplayString(convertedType, semanticModel, defaultExpression.SpanStart, SymbolDisplayFormats.DisplayName)}'",
                    ct =>
                    {
                        TypeSyntax newType = convertedType.ToTypeSyntax()
                                             .WithTriviaFrom(identifierName)
                                             .WithFormatterAndSimplifierAnnotation();

                        return(document.ReplaceNodeAsync(identifierName, newType, ct));
                    },
                    GetEquivalenceKey(diagnostic));

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

            case CompilerDiagnosticIdentifiers.CS1597_SemicolonAfterMethodOrAccessorBlockIsNotValid:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveSemicolon, document, root.SyntaxTree))
                {
                    break;
                }

                if (!token.IsKind(SyntaxKind.SemicolonToken))
                {
                    break;
                }

                switch (token.Parent)
                {
                case MethodDeclarationSyntax methodDeclaration:
                {
                    BlockSyntax body = methodDeclaration.Body;

                    if (body == null)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove semicolon",
                        ct =>
                            {
                                SyntaxTriviaList trivia = body
                                                          .GetTrailingTrivia()
                                                          .EmptyIfWhitespace()
                                                          .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                          .AddRange(token.TrailingTrivia);

                                MethodDeclarationSyntax newNode = methodDeclaration
                                                                  .WithBody(body.WithTrailingTrivia(trivia))
                                                                  .WithSemicolonToken(default(SyntaxToken));

                                return(document.ReplaceNodeAsync(methodDeclaration, newNode, ct));
                            },
                        GetEquivalenceKey(diagnostic));

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

                case PropertyDeclarationSyntax propertyDeclaration:
                {
                    AccessorListSyntax accessorList = propertyDeclaration.AccessorList;

                    if (accessorList == null)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove semicolon",
                        ct =>
                            {
                                SyntaxTriviaList trivia = accessorList
                                                          .GetTrailingTrivia()
                                                          .EmptyIfWhitespace()
                                                          .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                          .AddRange(token.TrailingTrivia);

                                PropertyDeclarationSyntax newNode = propertyDeclaration
                                                                    .WithAccessorList(accessorList.WithTrailingTrivia(trivia))
                                                                    .WithSemicolonToken(default(SyntaxToken));

                                return(document.ReplaceNodeAsync(propertyDeclaration, newNode, ct));
                            },
                        GetEquivalenceKey(diagnostic));

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

                case AccessorDeclarationSyntax accessorDeclaration:
                {
                    BlockSyntax body = accessorDeclaration.Body;

                    if (body == null)
                    {
                        break;
                    }

                    CodeAction codeAction = CodeAction.Create(
                        "Remove semicolon",
                        ct =>
                            {
                                SyntaxTriviaList trivia = body
                                                          .GetTrailingTrivia()
                                                          .EmptyIfWhitespace()
                                                          .AddRange(token.LeadingTrivia.EmptyIfWhitespace())
                                                          .AddRange(token.TrailingTrivia);

                                AccessorDeclarationSyntax newNode = accessorDeclaration
                                                                    .WithBody(body.WithTrailingTrivia(trivia))
                                                                    .WithSemicolonToken(default(SyntaxToken));

                                return(document.ReplaceNodeAsync(accessorDeclaration, newNode, ct));
                            },
                        GetEquivalenceKey(diagnostic));

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

                break;
            }

            case CompilerDiagnosticIdentifiers.CS0030_CannotConvertType:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeForEachType, document, root.SyntaxTree))
                {
                    break;
                }

                if (!token.IsKind(SyntaxKind.ForEachKeyword))
                {
                    break;
                }

                if (token.Parent is not ForEachStatementSyntax forEachStatement)
                {
                    break;
                }

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

                ForEachStatementInfo info = semanticModel.GetForEachStatementInfo(forEachStatement);

                ITypeSymbol typeSymbol = info.ElementType;

                if (typeSymbol.SupportsExplicitDeclaration())
                {
                    CodeFixRegistrator.ChangeType(context, diagnostic, forEachStatement.Type, typeSymbol, semanticModel, CodeFixIdentifiers.ChangeForEachType);
                }

                CodeFixRegistrator.ChangeTypeToVar(context, diagnostic, forEachStatement.Type, CodeFixIdentifiers.ChangeTypeToVar);
                break;
            }

            case CompilerDiagnosticIdentifiers.CS1737_OptionalParametersMustAppearAfterAllRequiredParameters:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddDefaultValueToParameter, document, root.SyntaxTree))
                {
                    break;
                }

                if (token.Parent is not BaseParameterListSyntax parameterList)
                {
                    break;
                }

                SeparatedSyntaxList <ParameterSyntax> parameters = parameterList.Parameters;

                ParameterSyntax parameter = null;

                for (int i = 0; i < parameters.Count; i++)
                {
                    ParameterSyntax p = parameters[i];

                    if (p.FullSpan.End <= token.SpanStart)
                    {
                        parameter = p;
                    }
                    else
                    {
                        break;
                    }
                }

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

                IParameterSymbol parameterSymbol = semanticModel.GetDeclaredSymbol(parameter, context.CancellationToken);

                ITypeSymbol typeSymbol = parameterSymbol.Type;

                if (typeSymbol.Kind == SymbolKind.ErrorType)
                {
                    break;
                }

                CodeAction codeAction = CodeAction.Create(
                    "Add default value",
                    ct =>
                    {
                        ExpressionSyntax defaultValue = typeSymbol.GetDefaultValueSyntax(document.GetDefaultSyntaxOptions());

                        ParameterSyntax newParameter = parameter
                                                       .WithDefault(EqualsValueClause(defaultValue).WithTrailingTrivia(parameter.GetTrailingTrivia()))
                                                       .WithoutTrailingTrivia()
                                                       .WithFormatterAnnotation();

                        return(document.ReplaceNodeAsync(parameter, newParameter, ct));
                    },
                    GetEquivalenceKey(diagnostic));

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

            case CompilerDiagnosticIdentifiers.CS8632_AnnotationForNullableReferenceTypesShouldOnlyBeUsedWithinNullableAnnotationsContext:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveAnnotationForNullableReferenceTypes, document, root.SyntaxTree))
                {
                    break;
                }

                if (!token.IsKind(SyntaxKind.QuestionToken))
                {
                    return;
                }

                CodeAction codeAction = CodeAction.Create(
                    "Remove 'nullable' annotation",
                    ct =>
                    {
                        var textChange = new TextChange(token.Span, "");

                        return(document.WithTextChangeAsync(textChange, ct));
                    },
                    GetEquivalenceKey(diagnostic));

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

            case CompilerDiagnosticIdentifiers.CS8602_DereferenceOfPossiblyNullReference:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree))
                {
                    break;
                }

                Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString());

                if (!token.IsKind(SyntaxKind.IdentifierToken))
                {
                    return;
                }

                if (!token.IsParentKind(SyntaxKind.IdentifierName))
                {
                    return;
                }

                SyntaxNode node = token.Parent
                                  .WalkUp(f => f.IsKind(SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.InvocationExpression, SyntaxKind.ElementAccessExpression))
                                  .Parent;

                if (node.IsKind(SyntaxKind.ExpressionStatement) &&
                    token.SpanStart == node.SpanStart)
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Use null propagation operator",
                        ct =>
                        {
                            var textChange = new TextChange(new TextSpan(token.Span.End, 0), "?");

                            return(document.WithTextChangeAsync(textChange, ct));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }

                return;
            }

            case CompilerDiagnosticIdentifiers.CS8604_PossibleNullReferenceArgumentForParameter:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree))
                {
                    break;
                }

                Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString());

                if (!token.IsKind(SyntaxKind.IdentifierToken))
                {
                    return;
                }

                if (!token.IsParentKind(SyntaxKind.IdentifierName))
                {
                    return;
                }

                CodeAction codeAction = CodeAction.Create(
                    "Use null-forgiving operator",
                    ct =>
                    {
                        var identifierName = (IdentifierNameSyntax)token.Parent;

                        PostfixUnaryExpressionSyntax newExpression = SuppressNullableWarningExpression(identifierName.WithoutTrivia())
                                                                     .WithTriviaFrom(identifierName);

                        return(document.ReplaceNodeAsync(identifierName, newExpression, ct));
                    },
                    GetEquivalenceKey(diagnostic));

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

            case CompilerDiagnosticIdentifiers.CS8618_NonNullableMemberIsUninitialized:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseNullForgivingOperator, document, root.SyntaxTree))
                {
                    break;
                }

                Debug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token.Kind().ToString());

                if (!token.IsKind(SyntaxKind.IdentifierToken))
                {
                    return;
                }

                if (token.IsParentKind(SyntaxKind.PropertyDeclaration))
                {
                    CodeAction codeAction = CodeAction.Create(
                        "Use null-forgiving operator",
                        ct =>
                        {
                            var property = (PropertyDeclarationSyntax)token.Parent;

                            PropertyDeclarationSyntax newProperty = property
                                                                    .WithoutTrailingTrivia()
                                                                    .WithInitializer(EqualsValueClause(SuppressNullableWarningExpression(NullLiteralExpression())))
                                                                    .WithSemicolonToken(SemicolonToken().WithTrailingTrivia(property.GetTrailingTrivia()));

                            return(document.ReplaceNodeAsync(property, newProperty, ct));
                        },
                        GetEquivalenceKey(diagnostic));

                    context.RegisterCodeFix(codeAction, diagnostic);
                }
                else
                {
                    SyntaxDebug.Assert(
                        (token.IsParentKind(SyntaxKind.VariableDeclarator) &&
                         token.Parent.IsParentKind(SyntaxKind.VariableDeclaration) &&
                         token.Parent.Parent.IsParentKind(SyntaxKind.FieldDeclaration, SyntaxKind.EventFieldDeclaration)) ||
                        token.IsParentKind(SyntaxKind.ConstructorDeclaration),
                        token);

                    if (token.IsParentKind(SyntaxKind.VariableDeclarator) &&
                        token.Parent.IsParentKind(SyntaxKind.VariableDeclaration) &&
                        token.Parent.Parent.IsParentKind(SyntaxKind.FieldDeclaration))
                    {
                        CodeAction codeAction = CodeAction.Create(
                            "Use null-forgiving operator",
                            ct =>
                            {
                                var declarator = (VariableDeclaratorSyntax)token.Parent;

                                VariableDeclaratorSyntax newDeclarator = declarator
                                                                         .WithoutTrailingTrivia()
                                                                         .WithInitializer(
                                    EqualsValueClause(SuppressNullableWarningExpression(NullLiteralExpression()))
                                    .WithTrailingTrivia(declarator.GetTrailingTrivia()));

                                return(document.ReplaceNodeAsync(declarator, newDeclarator, ct));
                            },
                            GetEquivalenceKey(diagnostic));

                        context.RegisterCodeFix(codeAction, diagnostic);
                    }
                }

                break;
            }

            case CompilerDiagnosticIdentifiers.CS8403_MethodWithIteratorBlockMustBeAsyncToReturnIAsyncEnumerableOfT:
            {
                if (!IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddAsyncModifier, document, root.SyntaxTree))
                {
                    break;
                }

                SyntaxDebug.Assert(token.IsKind(SyntaxKind.IdentifierToken), token);
                SyntaxDebug.Assert(token.IsParentKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement), token.Parent);

                if (token.IsParentKind(SyntaxKind.MethodDeclaration, SyntaxKind.LocalFunctionStatement))
                {
                    ModifiersCodeFixRegistrator.AddModifier(
                        context,
                        diagnostic,
                        token.Parent,
                        SyntaxKind.AsyncKeyword,
                        additionalKey: CodeFixIdentifiers.AddAsyncModifier);
                }

                break;
            }
            }
        }