File: Diagnostics\NonLocalDiagnosticTests.cs
Web Access
Project: src\src\LanguageServer\ProtocolUnitTests\Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests.csproj (Microsoft.CodeAnalysis.LanguageServer.Protocol.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.
 
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Public;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Diagnostics
{
    public class NonLocalDiagnosticTests : AbstractPullDiagnosticTestsBase
    {
        public NonLocalDiagnosticTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
        {
        }
 
        [Theory, CombinatorialData]
        [WorkItem("https://github.com/dotnet/vscode-csharp/issues/5634")]
        internal async Task TestNonLocalDocumentDiagnosticsAreReportedWhenFSAEnabled(bool mutatingLspWorkspace, bool fsaEnabled)
        {
            var markup1 = @"class A { }";
            var markup2 = @"class B { }";
            var scope = fsaEnabled ? BackgroundAnalysisScope.FullSolution : BackgroundAnalysisScope.OpenFiles;
            await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(
                 new[] { markup1, markup2 }, mutatingLspWorkspace, scope, useVSDiagnostics: false);
 
            var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.First();
 
            // Non-local document diagnostics are reported only for open documents by DocumentPullDiagnosticsHandler.
            // For closed documents, non-local document diagnostics are reported by the WorkspacePullDiagnosticsHandler
            // and not reported here.
            await OpenDocumentAsync(testLspServer, document);
 
            var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics: false, category: PublicDocumentNonLocalDiagnosticSourceProvider.NonLocal);
            if (fsaEnabled)
            {
                Assert.Equal(1, results.Length);
                Assert.Equal(2, results[0].Diagnostics?.Length);
                var orderedDiagnostics = results[0].Diagnostics.OrderBy(d => d.Code!.Value.Value).ToList();
                Assert.Equal(NonLocalDiagnosticsAnalyzer.NonLocalDescriptor.Id, orderedDiagnostics[0].Code);
                Assert.Equal(NonLocalDiagnosticsAnalyzer.CompilationEndDescriptor.Id, orderedDiagnostics[1].Code);
                Assert.Equal(document.GetURI(), results[0].Uri);
 
                // Asking again should give us back unchanged diagnostics.
                var results2 = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics: false, previousResultId: results.Single().ResultId, category: PublicDocumentNonLocalDiagnosticSourceProvider.NonLocal);
                Assert.Null(results2[0].Diagnostics);
                Assert.Equal(results[0].ResultId, results2[0].ResultId);
            }
            else
            {
                Assert.Empty(results);
 
                // Asking again should give us back unchanged diagnostics.
                var results2 = await RunGetDocumentPullDiagnosticsAsync(testLspServer, document.GetURI(), useVSDiagnostics: false, category: PublicDocumentNonLocalDiagnosticSourceProvider.NonLocal);
                Assert.Empty(results2);
            }
        }
 
        protected override TestComposition Composition => base.Composition.AddParts(typeof(NonLocalDiagnosticsAnalyzer));
 
        private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference()
            => new(ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>.Empty.Add(LanguageNames.CSharp, ImmutableArray.Create(
                DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp),
                new NonLocalDiagnosticsAnalyzer())));
 
        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        private sealed class NonLocalDiagnosticsAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor NonLocalDescriptor = new("NonLocal0001", "Title1", "NonLocal0001", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);
            public static readonly DiagnosticDescriptor CompilationEndDescriptor = new("NonLocal0002", "Title2", "NonLocal0002", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, customTags: [WellKnownDiagnosticTags.CompilationEnd]);
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(NonLocalDescriptor, CompilationEndDescriptor);
 
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(context =>
                {
                    var compilation = context.Compilation;
                    context.RegisterSyntaxTreeAction(context =>
                    {
                        foreach (var tree in compilation.SyntaxTrees)
                        {
                            if (tree != context.Tree)
                            {
                                var root = tree.GetRoot();
                                var diagnostic = Diagnostic.Create(NonLocalDescriptor, root.GetFirstToken().GetLocation());
                                context.ReportDiagnostic(diagnostic);
                            }
                        }
                    });
 
                    context.RegisterCompilationEndAction(context =>
                    {
                        foreach (var tree in compilation.SyntaxTrees)
                        {
                            var root = tree.GetRoot();
                            var diagnostic = Diagnostic.Create(CompilationEndDescriptor, root.GetFirstToken().GetLocation());
                            context.ReportDiagnostic(diagnostic);
                        }
                    });
                });
            }
        }
    }
}