File: CodeFixes\Suppression\AbstractSuppressionCodeFixProvider.AbstractGlobalSuppressMessageCodeAction.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.LanguageService;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CodeFixes.Suppression;
 
internal abstract partial class AbstractSuppressionCodeFixProvider : IConfigurationFixProvider
{
    internal abstract class AbstractGlobalSuppressMessageCodeAction : AbstractSuppressionCodeAction
    {
        private readonly Project _project;
 
        protected AbstractGlobalSuppressMessageCodeAction(AbstractSuppressionCodeFixProvider fixer, Project project)
            : base(fixer, title: FeaturesResources.in_Suppression_File)
        {
            _project = project;
        }
 
        protected sealed override async Task<ImmutableArray<CodeActionOperation>> ComputeOperationsAsync(
            IProgress<CodeAnalysisProgress> progress, CancellationToken cancellationToken)
        {
            var changedSuppressionDocument = await GetChangedSuppressionDocumentAsync(cancellationToken).ConfigureAwait(false);
            return
            [
                new ApplyChangesOperation(changedSuppressionDocument.Project.Solution),
                new OpenDocumentOperation(changedSuppressionDocument.Id, activateIfAlreadyOpen: true),
                new DocumentNavigationOperation(changedSuppressionDocument.Id, position: 0),
            ];
        }
 
        protected abstract Task<Document> GetChangedSuppressionDocumentAsync(CancellationToken cancellationToken);
 
        private string GetSuppressionsFilePath(string suppressionsFileName)
        {
            if (!string.IsNullOrEmpty(_project.FilePath))
            {
                var fullPath = Path.GetFullPath(_project.FilePath);
                var directory = PathUtilities.GetDirectoryName(fullPath);
                if (!string.IsNullOrEmpty(directory))
                {
                    var suppressionsFilePath = PathUtilities.CombinePossiblyRelativeAndRelativePaths(directory, suppressionsFileName);
                    if (!string.IsNullOrEmpty(suppressionsFilePath))
                    {
                        return suppressionsFilePath;
                    }
                }
            }
 
            return suppressionsFileName;
        }
 
        protected async Task<Document> GetOrCreateSuppressionsDocumentAsync(CancellationToken c)
        {
            var index = 1;
            var suppressionsFileName = s_globalSuppressionsFileName + Fixer.DefaultFileExtension;
            var suppressionsFilePath = GetSuppressionsFilePath(suppressionsFileName);
 
            Document suppressionsDoc = null;
            while (suppressionsDoc == null)
            {
                var hasDocWithSuppressionsName = false;
                foreach (var document in _project.Documents)
                {
                    var filePath = document.FilePath;
                    var fullPath = !string.IsNullOrEmpty(filePath) ? Path.GetFullPath(filePath) : filePath;
                    if (fullPath == suppressionsFilePath)
                    {
                        // Existing global suppressions file. See if this file only has imports and global assembly
                        // attributes.
                        hasDocWithSuppressionsName = true;
 
                        var t = await document.GetSyntaxTreeAsync(c).ConfigureAwait(false);
                        var r = await t.GetRootAsync(c).ConfigureAwait(false);
                        var syntaxFacts = _project.Services.GetRequiredService<ISyntaxFactsService>();
 
                        if (r.ChildNodes().All(n => syntaxFacts.IsUsingOrExternOrImport(n) || Fixer.IsAttributeListWithAssemblyAttributes(n)))
                        {
                            suppressionsDoc = document;
                            break;
                        }
                    }
                }
 
                if (suppressionsDoc == null)
                {
                    if (hasDocWithSuppressionsName || File.Exists(suppressionsFilePath))
                    {
                        index++;
                        suppressionsFileName = s_globalSuppressionsFileName + index.ToString() + Fixer.DefaultFileExtension;
                        suppressionsFilePath = GetSuppressionsFilePath(suppressionsFileName);
                    }
                    else
                    {
                        // Create an empty global suppressions file.
                        suppressionsDoc = _project.AddDocument(suppressionsFileName, string.Empty);
                    }
                }
            }
 
            return suppressionsDoc;
        }
    }
}