File: Microsoft.NetCore.Analyzers\Performance\UseStringContainsCharOverloadWithSingleCharacters.Fixer.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.NetAnalyzers;

namespace Microsoft.NetCore.Analyzers.Performance
{
    public abstract class UseStringContainsCharOverloadWithSingleCharactersCodeFix : SyntaxEditorBasedCodeFixProvider
    {
        public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(
            UseStringContainsCharOverloadWithSingleCharactersAnalyzer.CA1847);

        public override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
            var violatingNode = root.FindNode(context.Span, getInnermostNodeForTie: true);

            if (!TryGetLiteralValueFromNode(violatingNode, out _))
            {
                return;
            }

            RegisterCodeFix(context,
                MicrosoftNetCoreAnalyzersResources.ReplaceStringLiteralWithCharLiteralCodeActionTitle,
                nameof(MicrosoftNetCoreAnalyzersResources.ReplaceStringLiteralWithCharLiteralCodeActionTitle));
        }

        protected sealed override Task ApplyFixAsync(Document document, Diagnostic diagnostic, SyntaxEditor editor, CancellationToken cancellationToken)
        {
            var violatingNode = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
            if (!TryGetLiteralValueFromNode(violatingNode, out var sourceCharLiteral))
            {
                return Task.CompletedTask;
            }

            // The replacement is a fresh literal and a string literal has no diagnosable descendants,
            // so nothing of the original node is carried over.
            var newExpression = editor.Generator.LiteralExpression(sourceCharLiteral);
            if (TryGetArgumentName(violatingNode, out var argumentName))
            {
                newExpression = editor.Generator.Argument(argumentName, RefKind.None, newExpression);
            }

            editor.ReplaceNode(violatingNode, newExpression);

            return Task.CompletedTask;
        }

        protected abstract bool TryGetArgumentName(SyntaxNode violatingNode, out string argumentName);
        protected abstract bool TryGetLiteralValueFromNode(SyntaxNode violatingNode, out char charLiteral);
    }
}