File: Diagnostics\DiagnosticAnalyzerServiceTests.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.EditorFeatures.UnitTests)
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.CSharp;
using Microsoft.CodeAnalysis.Editor.Test;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Remote.Diagnostics;
using Microsoft.CodeAnalysis.Remote.Testing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Test.Utilities.TestGenerators;
using Roslyn.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.CommonDiagnosticAnalyzers;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics;
 
[UseExportProvider]
public class DiagnosticAnalyzerServiceTests
{
    private static readonly TestComposition s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures
        .AddParts(typeof(TestDocumentTrackingService));
 
    private static readonly TestComposition s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures;
 
    private static AdhocWorkspace CreateWorkspace(Type[] additionalParts = null)
        => new AdhocWorkspace(s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(additionalParts).GetHostServices());
 
    private static IGlobalOptionService GetGlobalOptions(Workspace workspace)
        => workspace.Services.SolutionServices.ExportProvider.GetExportedValue<IGlobalOptionService>();
 
    private static void OpenDocumentAndMakeActive(Document document, Workspace workspace)
    {
        workspace.OpenDocument(document.Id);
 
        var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetRequiredService<IDocumentTrackingService>();
        documentTrackingService.SetActiveDocument(document.Id);
    }
 
    [Fact]
    public async Task TestHasSuccessfullyLoadedBeingFalse()
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new Analyzer()));
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var document = GetDocumentFromIncompleteProject(workspace);
 
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
        var analyzer = service.CreateIncrementalAnalyzer(workspace);
        var globalOptions = exportProvider.GetExportedValue<IGlobalOptionService>();
 
        var diagnostics = await analyzer.GetDiagnosticsForIdsAsync(
            workspace.CurrentSolution, projectId: null, documentId: null, diagnosticIds: null, shouldIncludeAnalyzer: null, getDocuments: null,
            includeSuppressedDiagnostics: false, includeLocalDocumentDiagnostics: true, includeNonLocalDocumentDiagnostics: false, CancellationToken.None);
        Assert.NotEmpty(diagnostics);
    }
 
    [Fact]
    public async Task TestHasSuccessfullyLoadedBeingFalseFSAOn()
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new Analyzer()));
 
        var globalOptions = GetGlobalOptions(workspace);
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution);
 
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
        var document = GetDocumentFromIncompleteProject(workspace);
 
        OpenDocumentAndMakeActive(document, workspace);
 
        await TestAnalyzerAsync(workspace, document, AnalyzerResultSetter, expectedSyntax: true, expectedSemantic: true);
    }
 
    [Fact]
    public async Task TestHasSuccessfullyLoadedBeingFalseWhenFileOpened()
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new Analyzer()));
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var document = GetDocumentFromIncompleteProject(workspace);
 
        OpenDocumentAndMakeActive(document, workspace);
 
        await TestAnalyzerAsync(workspace, document, AnalyzerResultSetter, expectedSyntax: true, expectedSemantic: true);
    }
 
    [Fact]
    public async Task TestHasSuccessfullyLoadedBeingFalseWhenFileOpenedWithCompilerAnalyzer()
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new CSharpCompilerDiagnosticAnalyzer()));
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var document = GetDocumentFromIncompleteProject(workspace);
 
        // open document
        workspace.OpenDocument(document.Id);
 
        await TestAnalyzerAsync(workspace, document, CompilerAnalyzerResultSetter, expectedSyntax: true, expectedSemantic: false);
    }
 
    [Fact]
    public async Task TestHasSuccessfullyLoadedBeingFalseWithCompilerAnalyzerFSAOn()
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new CSharpCompilerDiagnosticAnalyzer()));
 
        var globalOptions = GetGlobalOptions(workspace);
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution);
 
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var document = GetDocumentFromIncompleteProject(workspace);
 
        await TestAnalyzerAsync(workspace, document, CompilerAnalyzerResultSetter, expectedSyntax: true, expectedSemantic: false);
    }
 
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enabledWithEditorconfig)
    {
        using var workspace = CreateWorkspace();
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(new DisabledByDefaultAnalyzer()));
 
        var globalOptions = GetGlobalOptions(workspace);
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution);
 
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var project = workspace.AddProject(
            ProjectInfo.Create(
                ProjectId.CreateNewId(),
                VersionStamp.Create(),
                "CSharpProject",
                "CSharpProject",
                LanguageNames.CSharp,
                filePath: "z:\\CSharpProject.csproj"));
 
        if (enabledWithEditorconfig)
        {
            var editorconfigText = @$"
[*.cs]
dotnet_diagnostic.{DisabledByDefaultAnalyzer.s_syntaxRule.Id}.severity = warning
dotnet_diagnostic.{DisabledByDefaultAnalyzer.s_semanticRule.Id}.severity = warning
dotnet_diagnostic.{DisabledByDefaultAnalyzer.s_compilationRule.Id}.severity = warning";
 
            project = project.AddAnalyzerConfigDocument(".editorconfig", filePath: "z:\\.editorconfig", text: SourceText.From(editorconfigText)).Project;
        }
 
        var document = project.AddDocument("test.cs", SourceText.From("class A {}"), filePath: "z:\\test.cs");
        var applied = workspace.TryApplyChanges(document.Project.Solution);
        Assert.True(applied);
 
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
        var analyzer = service.CreateIncrementalAnalyzer(workspace);
 
        // listen to events
        var syntaxDiagnostic = false;
        var semanticDiagnostic = false;
        var compilationDiagnostic = false;
 
        // open document
        workspace.OpenDocument(document.Id);
 
        var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None);
 
        foreach (var diagnostic in diagnostics)
        {
            if (diagnostic.Id == DisabledByDefaultAnalyzer.s_syntaxRule.Id)
            {
                syntaxDiagnostic = true;
            }
            else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_semanticRule.Id)
            {
                semanticDiagnostic = true;
            }
            else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_compilationRule.Id)
            {
                compilationDiagnostic = true;
            }
        }
 
        Assert.Equal(enabledWithEditorconfig, syntaxDiagnostic);
        Assert.Equal(enabledWithEditorconfig, semanticDiagnostic);
        Assert.Equal(enabledWithEditorconfig, compilationDiagnostic);
    }
 
    private static async Task TestAnalyzerAsync(
        AdhocWorkspace workspace,
        Document document,
        Func<bool, bool, ImmutableArray<DiagnosticData>, (bool, bool)> resultSetter,
        bool expectedSyntax, bool expectedSemantic)
    {
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
 
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
 
        var analyzer = service.CreateIncrementalAnalyzer(workspace);
 
        var syntax = false;
        var semantic = false;
 
        var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None);
 
        (syntax, semantic) = resultSetter(syntax, semantic, diagnostics);
 
        // two should have been called.
        Assert.Equal(expectedSyntax, syntax);
        Assert.Equal(expectedSemantic, semantic);
    }
 
    [Fact]
    public async Task TestHostAnalyzerOrderingAsync()
    {
        using var workspace = CreateWorkspace();
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(
            new Priority20Analyzer(),
            new Priority15Analyzer(),
            new Priority10Analyzer(),
            new Priority1Analyzer(),
            new Priority0Analyzer(),
            new CSharpCompilerDiagnosticAnalyzer(),
            new Analyzer()
        ));
 
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var project = workspace.AddProject(
                      ProjectInfo.Create(
                          ProjectId.CreateNewId(),
                          VersionStamp.Create(),
                          "Dummy",
                          "Dummy",
                          LanguageNames.CSharp));
 
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
        var analyzers = await incrementalAnalyzer.GetAnalyzersTestOnlyAsync(project, CancellationToken.None).ConfigureAwait(false);
        var analyzersArray = analyzers.ToArray();
 
        AssertEx.Equal(
        [
            typeof(FileContentLoadAnalyzer),
            typeof(GeneratorDiagnosticsPlaceholderAnalyzer),
            typeof(CSharpCompilerDiagnosticAnalyzer),
            typeof(Analyzer),
            typeof(Priority0Analyzer),
            typeof(Priority1Analyzer),
            typeof(Priority10Analyzer),
            typeof(Priority15Analyzer),
            typeof(Priority20Analyzer)
        ], analyzersArray.Select(a => a.GetType()));
    }
 
    [Fact]
    public async Task TestHostAnalyzerErrorNotLeaking()
    {
        using var workspace = CreateWorkspace();
 
        var solution = workspace.CurrentSolution;
 
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(
            new LeakDocumentAnalyzer(), new LeakProjectAnalyzer()));
 
        var globalOptions = GetGlobalOptions(workspace);
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution);
 
        workspace.TryApplyChanges(solution.WithAnalyzerReferences([analyzerReference]));
 
        var projectId = ProjectId.CreateNewId();
        var project = workspace.AddProject(
                      ProjectInfo.Create(
                          projectId,
                          VersionStamp.Create(),
                          "Dummy",
                          "Dummy",
                          LanguageNames.CSharp,
                          documents: [
                              DocumentInfo.Create(
                                  DocumentId.CreateNewId(projectId),
                                  "test.cs",
                                  loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class A {}"), VersionStamp.Create(), filePath: "test.cs")),
                                  filePath: "test.cs")]));
 
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
        Assert.NotEmpty(diagnostics);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42353")]
    public async Task TestFullSolutionAnalysisForHiddenAnalyzers()
    {
        // By default, hidden analyzer does not execute in full solution analysis.
        using var workspace = CreateWorkspaceWithProjectAndAnalyzer(new NamedTypeAnalyzer(DiagnosticSeverity.Hidden));
        var project = workspace.CurrentSolution.Projects.Single();
 
        await TestFullSolutionAnalysisForProjectAsync(workspace, project, expectAnalyzerExecuted: false);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42353")]
    public async Task TestFullSolutionAnalysisForHiddenAnalyzers_SeverityInCompilationOptions()
    {
        // Escalating the analyzer to non-hidden effective severity through compilation options
        // ensures that analyzer executes in full solution analysis.
        using var workspace = CreateWorkspaceWithProjectAndAnalyzer(new NamedTypeAnalyzer(DiagnosticSeverity.Hidden));
        var project = workspace.CurrentSolution.Projects.Single();
 
        var newSpecificOptions = project.CompilationOptions.SpecificDiagnosticOptions.Add(NamedTypeAnalyzer.DiagnosticId, ReportDiagnostic.Warn);
        project = project.WithCompilationOptions(project.CompilationOptions.WithSpecificDiagnosticOptions(newSpecificOptions));
        await TestFullSolutionAnalysisForProjectAsync(workspace, project, expectAnalyzerExecuted: true);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42353")]
    public async Task TestFullSolutionAnalysisForHiddenAnalyzers_SeverityInAnalyzerConfigOptions()
    {
        using var workspace = CreateWorkspaceWithProjectAndAnalyzer(new NamedTypeAnalyzer(DiagnosticSeverity.Hidden));
        var project = workspace.CurrentSolution.Projects.Single();
 
        // Escalating the analyzer to non-hidden effective severity through analyzer config options
        // ensures that analyzer executes in full solution analysis.
        var analyzerConfigText = $@"
[*.cs]
dotnet_diagnostic.{NamedTypeAnalyzer.DiagnosticId}.severity = warning
";
 
        project = project.AddAnalyzerConfigDocument(
            ".editorconfig",
            text: SourceText.From(analyzerConfigText),
            filePath: "z:\\.editorconfig").Project;
 
        await TestFullSolutionAnalysisForProjectAsync(workspace, project, expectAnalyzerExecuted: true);
    }
 
    private static AdhocWorkspace CreateWorkspaceWithProjectAndAnalyzer(DiagnosticAnalyzer analyzer)
    {
        var workspace = CreateWorkspace();
 
        var globalOptions = GetGlobalOptions(workspace);
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution);
 
        var projectId = ProjectId.CreateNewId();
        var solution = workspace.CurrentSolution;
 
        solution = solution
            .AddAnalyzerReference(new AnalyzerImageReference(ImmutableArray.Create(analyzer)))
            .AddProject(
                ProjectInfo.Create(
                    projectId,
                    VersionStamp.Create(),
                    "Dummy",
                    "Dummy",
                    LanguageNames.CSharp,
                    filePath: "z:\\Dummy.csproj",
                    documents: [
                        DocumentInfo.Create(
                            DocumentId.CreateNewId(projectId),
                            "test.cs",
                            loader: TextLoader.From(TextAndVersion.Create(SourceText.From("class A {}"), VersionStamp.Create(), filePath: "test.cs")),
                            filePath: "z:\\test.cs")]));
 
        Assert.True(workspace.TryApplyChanges(solution));
 
        return workspace;
    }
 
    private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace workspace, Project project, bool expectAnalyzerExecuted)
    {
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(project.Solution.Workspace);
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
 
        if (expectAnalyzerExecuted)
        {
            Assert.NotEmpty(diagnostics);
        }
        else
        {
            Assert.Empty(diagnostics);
        }
    }
 
    [Theory, CombinatorialData]
    internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool testMultiple)
    {
        using var workspace = CreateWorkspace();
 
        var globalOptions = GetGlobalOptions(workspace);
 
        var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "CSharpProject", "CSharpProject", LanguageNames.CSharp);
        var project = workspace.AddProject(projectInfo);
 
        var diagnosticSpan = new TextSpan(2, 2);
        var analyzer = new AdditionalFileAnalyzer(registerFromInitialize, diagnosticSpan, id: "ID0001");
        var analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(analyzer);
        if (testMultiple)
        {
            analyzer = new AdditionalFileAnalyzer2(registerFromInitialize, diagnosticSpan, id: "ID0002");
            analyzers = analyzers.Add(analyzer);
        }
 
        var analyzerReference = new AnalyzerImageReference(analyzers);
        project = project.WithAnalyzerReferences([analyzerReference])
            .AddAdditionalDocument(name: "dummy.txt", text: "Additional File Text", filePath: "dummy.txt").Project;
        if (testMultiple)
        {
            project = project.AddAdditionalDocument(name: "dummy2.txt", text: "Additional File2 Text", filePath: "dummy2.txt").Project;
        }
 
        var applied = workspace.TryApplyChanges(project.Solution);
        Assert.True(applied);
 
        var exportProvider = workspace.Services.SolutionServices.ExportProvider;
        var service = Assert.IsType<DiagnosticAnalyzerService>(exportProvider.GetExportedValue<IDiagnosticAnalyzerService>());
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
        var firstAdditionalDocument = project.AdditionalDocuments.FirstOrDefault();
 
        workspace.OpenAdditionalDocument(firstAdditionalDocument.Id);
 
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
 
        var expectedCount = testMultiple ? 4 : 1;
 
        Assert.Equal(expectedCount, diagnostics.Length);
 
        for (var i = 0; i < analyzers.Length; i++)
        {
            analyzer = (AdditionalFileAnalyzer)analyzers[i];
            foreach (var additionalDoc in project.AdditionalDocuments)
            {
                var applicableDiagnostics = diagnostics.Where(
                    d => d.Id == analyzer.Descriptor.Id && d.DataLocation.UnmappedFileSpan.Path == additionalDoc.FilePath);
 
                var text = await additionalDoc.GetTextAsync();
 
                var diagnostic = Assert.Single(applicableDiagnostics);
                Assert.Equal(diagnosticSpan, diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text));
                diagnostics = diagnostics.Remove(diagnostic);
            }
        }
 
        Assert.Empty(diagnostics);
    }
 
    private class AdditionalFileAnalyzer2 : AdditionalFileAnalyzer
    {
        public AdditionalFileAnalyzer2(bool registerFromInitialize, TextSpan diagnosticSpan, string id)
            : base(registerFromInitialize, diagnosticSpan, id)
        {
        }
    }
 
    [Theory, CombinatorialData]
    internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeSuppressor, BackgroundAnalysisScope analysisScope)
    {
        var analyzers = ArrayBuilder<DiagnosticAnalyzer>.GetInstance();
        if (includeAnalyzer)
        {
            analyzers.Add(new NamedTypeAnalyzer());
        }
 
        if (includeSuppressor)
        {
            analyzers.Add(new DiagnosticSuppressorForId(NamedTypeAnalyzer.DiagnosticId));
        }
 
        var analyzerReference = new AnalyzerImageReference(analyzers.ToImmutableArray());
 
        using var workspace = EditorTestWorkspace.CreateCSharp("class A {}", composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService)));
 
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope);
 
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var project = workspace.CurrentSolution.Projects.Single();
        var document = project.Documents.Single();
 
        var service = Assert.IsType<DiagnosticAnalyzerService>(workspace.GetService<IDiagnosticAnalyzerService>());
        var globalOptions = workspace.GetService<IGlobalOptionService>();
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
 
        switch (analysisScope)
        {
            case BackgroundAnalysisScope.None:
            case BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics:
                workspace.OpenDocument(document.Id);
                var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetService<IDocumentTrackingService>();
                documentTrackingService.SetActiveDocument(document.Id);
                break;
 
            case BackgroundAnalysisScope.OpenFiles:
                workspace.OpenDocument(document.Id);
                break;
 
            case BackgroundAnalysisScope.FullSolution:
                break;
 
            default:
                throw ExceptionUtilities.UnexpectedValue(analysisScope);
        }
 
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
 
        var diagnostic = diagnostics.SingleOrDefault();
        if (includeAnalyzer)
        {
            Assert.True(diagnostic != null);
            Assert.Equal(NamedTypeAnalyzer.DiagnosticId, diagnostic.Id);
            Assert.Equal(includeSuppressor, diagnostic.IsSuppressed);
        }
        else
        {
            Assert.True(diagnostic == null);
        }
    }
 
    [Theory, CombinatorialData]
    internal async Task TestRemoveUnnecessaryInlineSuppressionsAnalyzer(BackgroundAnalysisScope analysisScope, bool isSourceGenerated, bool testPragma)
    {
        var analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(
            new CSharpCompilerDiagnosticAnalyzer(),
            new NamedTypeAnalyzer(),
            new CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer());
 
        var analyzerReference = new AnalyzerImageReference(analyzers);
 
        string code;
        if (testPragma)
        {
            code = $@"
#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary
#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary
 
#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Necessary
class A
{{
    void M()
    {{
#pragma warning disable CS0168 // Variable is declared but never used - Necessary
        int x;
    }}
}}
";
        }
        else
        {
            code = $@"
[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category1"", ""{NamedTypeAnalyzer.DiagnosticId}"")] // Necessary
class A
{{
    [System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")] // Unnecessary
    [System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")] // Unnecessary
    void M()
    {{
#pragma warning disable CS0168 // Variable is declared but never used - Necessary
        int x;
    }}
}}
";
        }
 
        string[] files;
        string[] sourceGeneratedFiles;
        if (isSourceGenerated)
        {
            files = [];
            sourceGeneratedFiles = [code];
        }
        else
        {
            files = [code];
            sourceGeneratedFiles = [];
        }
 
        var composition = s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(
            typeof(TestDocumentTrackingService));
 
        using var workspace = new EditorTestWorkspace(composition);
 
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope);
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.EnableDiagnosticsInSourceGeneratedFiles, isSourceGenerated);
 
        var compilerDiagnosticsScope = analysisScope.ToEquivalentCompilerDiagnosticsScope();
        workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption, LanguageNames.CSharp, compilerDiagnosticsScope);
 
        workspace.InitializeDocuments(TestWorkspace.CreateWorkspaceElement(LanguageNames.CSharp, files: files, sourceGeneratedFiles: sourceGeneratedFiles), openDocuments: false);
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var project = workspace.CurrentSolution.Projects.Single();
        var document = isSourceGenerated ? (await project.GetSourceGeneratedDocumentsAsync(CancellationToken.None)).Single() : project.Documents.Single();
        if (isSourceGenerated)
            Assert.IsType<SourceGeneratedDocument>(document);
        else
            Assert.IsType<Document>(document);
 
        var service = Assert.IsType<DiagnosticAnalyzerService>(workspace.GetService<IDiagnosticAnalyzerService>());
 
        var text = await document.GetTextAsync();
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
 
        switch (analysisScope)
        {
            case BackgroundAnalysisScope.None:
            case BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics:
                if (isSourceGenerated)
                    workspace.OpenSourceGeneratedDocument(document.Id);
                else
                    workspace.OpenDocument(document.Id);
 
                var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetRequiredService<IDocumentTrackingService>();
                documentTrackingService.SetActiveDocument(document.Id);
                break;
 
            case BackgroundAnalysisScope.OpenFiles:
                if (isSourceGenerated)
                    workspace.OpenSourceGeneratedDocument(document.Id);
                else
                    workspace.OpenDocument(document.Id);
 
                break;
 
            case BackgroundAnalysisScope.FullSolution:
                break;
        }
 
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
 
        diagnostics = diagnostics
            .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)
            .OrderBy(d => d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))
            .ToImmutableArray();
 
        var root = await document.GetSyntaxRootAsync();
        text = await document.GetTextAsync();
 
        Assert.Equal(2, diagnostics.Length);
        if (testPragma)
        {
            var pragma1 = root.FindTrivia(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString();
            Assert.Equal($"#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary", pragma1);
            var pragma2 = root.FindTrivia(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString();
            Assert.Equal($"#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary", pragma2);
        }
        else
        {
            var attribute1 = root.FindNode(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString();
            Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")", attribute1);
            var attribute2 = root.FindNode(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString();
            Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")", attribute2);
        }
    }
 
    [Theory, CombinatorialData]
    [WorkItem("https://github.com/dotnet/roslyn/issues/49698")]
    internal async Task TestOnlyRequiredAnalyzerExecutedDuringDiagnosticComputation(bool documentAnalysis)
    {
        using var workspace = TestWorkspace.CreateCSharp("class A { }");
 
        // Verify that requesting analyzer diagnostics for analyzer1 does not lead to invoking analyzer2.
        var analyzer1 = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false);
        var analyzer1Id = analyzer1.GetAnalyzerId();
        var analyzer2 = new NamedTypeAnalyzer();
        var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzer1Id);
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer1, analyzer2));
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
        var project = workspace.CurrentSolution.Projects.Single();
        var document = documentAnalysis ? project.Documents.Single() : null;
        var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync(
            document, project, Checksum.Null, span: null, projectAnalyzerIds: [], analyzerIdsToRequestDiagnostics,
            AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services,
            isExplicit: false, reportSuppressedDiagnostics: false, logPerformanceInfo: false, getTelemetryInfo: false,
            cancellationToken: CancellationToken.None);
        Assert.False(analyzer2.ReceivedSymbolCallback);
 
        Assert.Equal(1, diagnosticsMapResults.Diagnostics.Length);
        var (actualAnalyzerId, diagnosticMap) = diagnosticsMapResults.Diagnostics.Single();
        Assert.Equal(analyzer1Id, actualAnalyzerId);
        Assert.Equal(1, diagnosticMap.Semantic.Length);
        var semanticDiagnostics = diagnosticMap.Semantic.Single().Item2;
        var diagnostic = Assert.Single(semanticDiagnostics);
        Assert.Equal(analyzer1.Descriptor.Id, diagnostic.Id);
 
        Assert.Empty(diagnosticMap.Syntax);
        Assert.Empty(diagnosticMap.NonLocal);
        Assert.Empty(diagnosticMap.Other);
    }
 
    [Theory, WorkItem(67257, "https://github.com/dotnet/roslyn/issues/67257")]
    [CombinatorialData]
    public async Task TestFilterSpanOnContextAsync(FilterSpanTestAnalyzer.AnalysisKind kind)
    {
        var source = @"
class B
{
    void M()
    {
        int x = 1;
    }
}";
        var additionalText = @"This is an additional file!";
 
        using var workspace = TestWorkspace.CreateCSharp(source);
        var project = workspace.CurrentSolution.Projects.Single();
        project = project.AddAdditionalDocument("additional.txt", additionalText).Project;
 
        var analyzer = new FilterSpanTestAnalyzer(kind);
        var analyzerId = analyzer.GetAnalyzerId();
        var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzerId);
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
        project = project.AddAnalyzerReference(analyzerReference);
 
        workspace.TryApplyChanges(project.Solution);
 
        project = workspace.CurrentSolution.Projects.Single();
        var document = project.Documents.Single();
        var additionalDocument = project.AdditionalDocuments.Single();
 
        var root = await document.GetRequiredSyntaxRootAsync(CancellationToken.None);
        var localDeclaration = root.DescendantNodes().OfType<CodeAnalysis.CSharp.Syntax.LocalDeclarationStatementSyntax>().First();
        var filterSpan = kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile
            ? new TextSpan(0, 1)
            : localDeclaration.Span;
        // Invoke "GetDiagnosticsAsync" for a sub-span and then
        // for the entire document span and verify FilterSpan/FilterTree on the callback context.
        Assert.Null(analyzer.CallbackFilterSpan);
        Assert.Null(analyzer.CallbackFilterTree);
        await VerifyCallbackSpanAsync(filterSpan);
        await VerifyCallbackSpanAsync(filterSpan: null);
 
        async Task VerifyCallbackSpanAsync(TextSpan? filterSpan)
        {
            var analysisKind = kind is FilterSpanTestAnalyzer.AnalysisKind.SyntaxTree or FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile
                ? AnalysisKind.Syntax
                : AnalysisKind.Semantic;
            var documentToAnalyze = kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile ? additionalDocument : document;
            _ = await DiagnosticComputer.GetDiagnosticsAsync(
                documentToAnalyze, project, Checksum.Null, filterSpan, analyzerIdsToRequestDiagnostics, hostAnalyzerIds: [],
                analysisKind, new DiagnosticAnalyzerInfoCache(), workspace.Services,
                isExplicit: false, reportSuppressedDiagnostics: false, logPerformanceInfo: false, getTelemetryInfo: false,
                CancellationToken.None);
            Assert.Equal(filterSpan, analyzer.CallbackFilterSpan);
            if (kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile)
            {
                var expectedText = additionalDocument.GetTextSynchronously(CancellationToken.None).ToString();
                var actualText = analyzer.CallbackFilterFile.GetText().ToString();
                Assert.Equal(expectedText, actualText);
                Assert.Null(analyzer.CallbackFilterTree);
            }
            else
            {
                Assert.Equal(root.SyntaxTree, analyzer.CallbackFilterTree);
                Assert.Null(analyzer.CallbackFilterFile);
            }
        }
    }
 
    [Theory, CombinatorialData]
    [WorkItem("https://github.com/dotnet/roslyn/issues/67084")]
    internal async Task TestCancellationDuringDiagnosticComputation_OutOfProc(AnalyzerRegisterActionKind actionKind)
    {
        // This test verifies that we do no attempt to re-use CompilationWithAnalyzers instance in IDE OutOfProc diagnostic computation in presence of an OperationCanceledException during analysis.
        // Attempting to do so has led to large number of reliability issues and flakiness in diagnostic computation, which we want to avoid.
        // NOTE: Unfortunately, we cannot perform an end-to-end OutOfProc test, similar to the InProc test above because AnalyzerImageReference is not serializable.
        //       So, we perform a very targeted test which directly uses the 'DiagnosticComputer' type that is used for all OutOfProc diagnostic computation.
 
        var source = @"
class A
{
    void M()
    {
        int x = 0;
    }
}";
 
        using var workspace = TestWorkspace.CreateCSharp(source);
 
        var analyzer = new CancellationTestAnalyzer(actionKind);
        var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));
        workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference]));
 
        var project = workspace.CurrentSolution.Projects.Single();
        var document = project.Documents.Single();
        var diagnosticAnalyzerInfoCache = new DiagnosticAnalyzerInfoCache();
 
        var kind = actionKind == AnalyzerRegisterActionKind.SyntaxTree ? AnalysisKind.Syntax : AnalysisKind.Semantic;
        var analyzerIds = ImmutableArray.Create(analyzer.GetAnalyzerId());
 
        // First invoke analysis with cancellation token, and verify canceled compilation and no reported diagnostics.
        Assert.Empty(analyzer.CanceledCompilations);
        try
        {
            _ = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null,
                projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, isExplicit: false, reportSuppressedDiagnostics: false,
                logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: analyzer.CancellationToken);
 
            throw ExceptionUtilities.Unreachable();
        }
        catch (OperationCanceledException) when (analyzer.CancellationToken.IsCancellationRequested)
        {
        }
 
        Assert.Single(analyzer.CanceledCompilations);
 
        // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic.
        var diagnosticsMap = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null,
            projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, isExplicit: false, reportSuppressedDiagnostics: false,
            logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None);
        var builder = diagnosticsMap.Diagnostics.Single().diagnosticMap;
        var diagnostic = kind == AnalysisKind.Syntax ? builder.Syntax.Single().Item2.Single() : builder.Semantic.Single().Item2.Single();
        Assert.Equal(CancellationTestAnalyzer.DiagnosticId, diagnostic.Id);
    }
 
    [Theory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1909806")]
    [CombinatorialData]
    internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, bool analyzeProject, TestHost testHost)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp("// This file will get a diagnostic", composition: s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.WithTestHostParts(testHost));
 
        var globalOptions = workspace.GetService<IGlobalOptionService>();
 
        var generator = new DiagnosticProducingGenerator(c => Location.Create(c.Compilation.SyntaxTrees.Single(), new TextSpan(0, 10)));
        Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(new TestGeneratorReference(generator)).Solution));
 
        var project = workspace.CurrentSolution.Projects.Single();
        var document = project.Documents.Single();
 
        globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp,
            fullSolutionAnalysis ? BackgroundAnalysisScope.FullSolution : BackgroundAnalysisScope.OpenFiles);
 
        // If we aren't testing FSA or analyzing document diagnostics, then open the file.
        if (!fullSolutionAnalysis || !analyzeProject)
        {
            workspace.OpenDocument(document.Id);
        }
 
        var service = Assert.IsType<DiagnosticAnalyzerService>(workspace.GetService<IDiagnosticAnalyzerService>());
 
        var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace);
        var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None);
 
        Assert.NotEmpty(diagnostics);
    }
 
    private static Document GetDocumentFromIncompleteProject(AdhocWorkspace workspace)
    {
        var project = workspace.AddProject(
                        ProjectInfo.Create(
                            ProjectId.CreateNewId(),
                            VersionStamp.Create(),
                            "CSharpProject",
                            "CSharpProject",
                            LanguageNames.CSharp).WithHasAllInformation(hasAllInformation: false));
 
        return workspace.AddDocument(project.Id, "Empty.cs", SourceText.From("class A { B B {get} }"));
    }
 
    private static (bool, bool) AnalyzerResultSetter(bool syntax, bool semantic, ImmutableArray<DiagnosticData> diagnostics)
    {
        foreach (var diagnostic in diagnostics)
        {
            syntax |= diagnostic.Id == Analyzer.s_syntaxRule.Id;
            semantic |= diagnostic.Id == Analyzer.s_semanticRule.Id;
        }
 
        return (syntax, semantic);
    }
 
    private static (bool, bool) CompilerAnalyzerResultSetter(bool syntax, bool semantic, ImmutableArray<DiagnosticData> diagnostics)
    {
        syntax |= diagnostics.Any(d => d.Properties["Origin"] == "Syntactic");
        semantic |= diagnostics.Any(d => d.Properties["Origin"] != "Syntactic");
 
        return (syntax, semantic);
    }
 
    private class Analyzer : DiagnosticAnalyzer
    {
        internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
        internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
        internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_syntaxRule, s_semanticRule, s_compilationRule);
 
        public override void Initialize(AnalysisContext context)
        {
            context.RegisterSyntaxTreeAction(c => c.ReportDiagnostic(Diagnostic.Create(s_syntaxRule, c.Tree.GetRoot().GetLocation())));
            context.RegisterSemanticModelAction(c => c.ReportDiagnostic(Diagnostic.Create(s_semanticRule, c.SemanticModel.SyntaxTree.GetRoot().GetLocation())));
            context.RegisterCompilationAction(c => c.ReportDiagnostic(Diagnostic.Create(s_compilationRule, c.Compilation.SyntaxTrees.First().GetRoot().GetLocation())));
        }
    }
 
    private class DisabledByDefaultAnalyzer : DiagnosticAnalyzer
    {
        internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false);
        internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false);
        internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_syntaxRule, s_semanticRule, s_compilationRule);
 
        public override void Initialize(AnalysisContext context)
        {
            context.RegisterSyntaxTreeAction(c => c.ReportDiagnostic(Diagnostic.Create(s_syntaxRule, c.Tree.GetRoot().GetLocation())));
            context.RegisterSemanticModelAction(c => c.ReportDiagnostic(Diagnostic.Create(s_semanticRule, c.SemanticModel.SyntaxTree.GetRoot().GetLocation())));
            context.RegisterCompilationAction(c => c.ReportDiagnostic(Diagnostic.Create(s_compilationRule, c.Compilation.SyntaxTrees.First().GetRoot().GetLocation())));
        }
    }
 
    private class NoNameAnalyzer : DocumentDiagnosticAnalyzer
    {
        internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_syntaxRule);
 
        public override Task<ImmutableArray<Diagnostic>> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken)
            => Task.FromResult(ImmutableArray.Create(Diagnostic.Create(s_syntaxRule, Location.Create(document.FilePath, TextSpan.FromBounds(0, 0), new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 0))))));
 
        public override Task<ImmutableArray<Diagnostic>> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken)
            => SpecializedTasks.Default<ImmutableArray<Diagnostic>>();
    }
 
    private class Priority20Analyzer : PriorityTestDocumentDiagnosticAnalyzer
    {
        public Priority20Analyzer() : base(priority: 20) { }
    }
 
    private class Priority15Analyzer : PriorityTestProjectDiagnosticAnalyzer
    {
        public Priority15Analyzer() : base(priority: 15) { }
    }
 
    private class Priority10Analyzer : PriorityTestDocumentDiagnosticAnalyzer
    {
        public Priority10Analyzer() : base(priority: 10) { }
    }
 
    private class Priority1Analyzer : PriorityTestProjectDiagnosticAnalyzer
    {
        public Priority1Analyzer() : base(priority: 1) { }
    }
 
    private class Priority0Analyzer : PriorityTestDocumentDiagnosticAnalyzer
    {
        public Priority0Analyzer() : base(priority: -1) { }
    }
 
    private class PriorityTestDocumentDiagnosticAnalyzer : DocumentDiagnosticAnalyzer
    {
        protected PriorityTestDocumentDiagnosticAnalyzer(int priority)
            => Priority = priority;
 
        public override int Priority { get; }
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray<DiagnosticDescriptor>.Empty;
        public override Task<ImmutableArray<Diagnostic>> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken)
            => Task.FromResult(ImmutableArray<Diagnostic>.Empty);
        public override Task<ImmutableArray<Diagnostic>> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken)
            => Task.FromResult(ImmutableArray<Diagnostic>.Empty);
    }
 
    private class PriorityTestProjectDiagnosticAnalyzer : ProjectDiagnosticAnalyzer
    {
        protected PriorityTestProjectDiagnosticAnalyzer(int priority)
            => Priority = priority;
 
        public override int Priority { get; }
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray<DiagnosticDescriptor>.Empty;
        public override Task<ImmutableArray<Diagnostic>> AnalyzeProjectAsync(Project project, CancellationToken cancellationToken)
            => Task.FromResult(ImmutableArray<Diagnostic>.Empty);
    }
 
    private class LeakDocumentAnalyzer : DocumentDiagnosticAnalyzer
    {
        internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("leak", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_syntaxRule);
 
        public override async Task<ImmutableArray<Diagnostic>> AnalyzeSyntaxAsync(Document document, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            return ImmutableArray.Create(Diagnostic.Create(s_syntaxRule, root.GetLocation()));
        }
 
        public override Task<ImmutableArray<Diagnostic>> AnalyzeSemanticsAsync(Document document, CancellationToken cancellationToken)
            => SpecializedTasks.Default<ImmutableArray<Diagnostic>>();
    }
 
    private class LeakProjectAnalyzer : ProjectDiagnosticAnalyzer
    {
        private static readonly DiagnosticDescriptor s_rule = new DiagnosticDescriptor("project", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true);
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_rule);
        public override Task<ImmutableArray<Diagnostic>> AnalyzeProjectAsync(Project project, CancellationToken cancellationToken) => SpecializedTasks.Default<ImmutableArray<Diagnostic>>();
    }
 
    [DiagnosticAnalyzer(LanguageNames.CSharp)]
    private class NamedTypeAnalyzer : DiagnosticAnalyzer
    {
        public const string DiagnosticId = "test";
        private readonly ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics;
 
        public NamedTypeAnalyzer(DiagnosticSeverity defaultSeverity = DiagnosticSeverity.Warning)
            => _supportedDiagnostics = ImmutableArray.Create(new DiagnosticDescriptor(DiagnosticId, "test", "test", "test", defaultSeverity, isEnabledByDefault: true));
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => _supportedDiagnostics;
        public bool ReceivedSymbolCallback { get; private set; }
 
        public override void Initialize(AnalysisContext context)
        {
            context.RegisterSymbolAction(c =>
            {
                ReceivedSymbolCallback = true;
                c.ReportDiagnostic(Diagnostic.Create(_supportedDiagnostics[0], c.Symbol.Locations[0]));
            }, SymbolKind.NamedType);
        }
    }
}