File: Microsoft.NetCore.Analyzers\Runtime\UseEnvironmentMembersFixer.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.Composition;
using System.Threading;
using System.Threading.Tasks;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.NetAnalyzers;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.NetCore.Analyzers.Runtime
{
    /// <summary>CA1837, CA1839, CA1840: Use Environment.ProcessId / ProcessPath / CurrentManagedThreadId</summary>
    [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared]
    public sealed class UseEnvironmentMembersFixer : CodeFixProvider
    {
        public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(
            UseEnvironmentMembers.EnvironmentProcessIdRuleId,
            UseEnvironmentMembers.EnvironmentProcessPathRuleId,
            UseEnvironmentMembers.EnvironmentCurrentManagedThreadIdRuleId);

        // One title per rule, so a fix-all pass has to apply only the rule the user invoked it from.
        public sealed override FixAllProvider GetFixAllProvider()
            => SyntaxEditorFixAllProvider.Create<string?>(
                static fixAllContext => fixAllContext.CodeActionEquivalenceKey,
                static (document, diagnostic, editor, equivalenceKey, cancellationToken) =>
                    equivalenceKey is null || equivalenceKey == GetTitle(diagnostic.Id)
                        ? ApplyFixAsync(document, diagnostic, editor, cancellationToken)
                        : Task.CompletedTask);

        public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            Document doc = context.Document;
            SemanticModel model = await doc.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
            SyntaxNode root = await doc.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            if (root.FindNode(context.Span, getInnermostNodeForTie: true) is SyntaxNode node &&
                model.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemEnvironment, out _) &&
                model.GetOperation(node, context.CancellationToken) is IPropertyReferenceOperation)
            {
                string title = GetTitle(context.Diagnostics[0].Id);

                context.RegisterCodeFix(
                    CodeAction.Create(
                        title,
                        cancellationToken => SyntaxEditorFixAllProvider.ApplyFixesAsync(doc, context.Diagnostics, ApplyFixAsync, cancellationToken),
                        equivalenceKey: title),
                    context.Diagnostics);
            }
        }

        private static string GetTitle(string ruleId) => ruleId switch
        {
            UseEnvironmentMembers.EnvironmentProcessIdRuleId => MicrosoftNetCoreAnalyzersResources.UseEnvironmentProcessIdFix,
            UseEnvironmentMembers.EnvironmentProcessPathRuleId => MicrosoftNetCoreAnalyzersResources.UseEnvironmentProcessPathFix,
            _ => MicrosoftNetCoreAnalyzersResources.UseEnvironmentCurrentManagedThreadIdFix,
        };

        private static string GetMemberName(string ruleId) => ruleId switch
        {
            UseEnvironmentMembers.EnvironmentProcessIdRuleId => "ProcessId",
            UseEnvironmentMembers.EnvironmentProcessPathRuleId => "ProcessPath",
            _ => "CurrentManagedThreadId",
        };

        private static async Task ApplyFixAsync(Document document, Diagnostic diagnostic, SyntaxEditor editor, CancellationToken cancellationToken)
        {
            SemanticModel model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
            SyntaxNode node = editor.OriginalRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);

            if (!model.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemEnvironment, out INamedTypeSymbol? environmentType) ||
                model.GetOperation(node, cancellationToken) is not IPropertyReferenceOperation)
            {
                return;
            }

            SyntaxNode replacement = editor.Generator.MemberAccessExpression(
                editor.Generator.TypeExpressionForStaticMemberAccess(environmentType),
                GetMemberName(diagnostic.Id));

            editor.ReplaceNode(node, replacement.WithTriviaFrom(node));
        }
    }
}