File: Diagnostics\BuildOnlyDiagnosticIdsHandlerTests.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.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
 
namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.Completion
{
    public class BuildOnlyDiagnosticIdsHandlerTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper)
    {
        [Theory, CombinatorialData]
        [WorkItem("https://github.com/dotnet/vscode-csharp/issues/5728")]
        public async Task TestCSharpBuildOnlyDiagnosticIdsAsync(bool mutatingLspWorkspace)
        {
            await using var testLspServer = await CreateTestLspServerAsync("class C { }", mutatingLspWorkspace);
 
            var result = await testLspServer.ExecuteRequest0Async<BuildOnlyDiagnosticIdsResult>(BuildOnlyDiagnosticIdsHandler.BuildOnlyDiagnosticIdsMethodName,
                CancellationToken.None);
 
            var expectedBuildOnlyDiagnosticIds = GetExpectedBuildOnlyDiagnosticIds(LanguageNames.CSharp);
            AssertEx.SetEqual(expectedBuildOnlyDiagnosticIds, result.Ids);
        }
 
        [Theory, CombinatorialData]
        [WorkItem("https://github.com/dotnet/vscode-csharp/issues/5728")]
        public async Task TestVisualBasicBuildOnlyDiagnosticIdsAsync(bool mutatingLspWorkspace)
        {
            await using var testLspServer = await CreateVisualBasicTestLspServerAsync(markup: @"
Class C
End Class", mutatingLspWorkspace);
 
            var result = await testLspServer.ExecuteRequest0Async<BuildOnlyDiagnosticIdsResult>(BuildOnlyDiagnosticIdsHandler.BuildOnlyDiagnosticIdsMethodName,
                CancellationToken.None);
 
            var expectedBuildOnlyDiagnosticIds = GetExpectedBuildOnlyDiagnosticIds(LanguageNames.VisualBasic);
            AssertEx.SetEqual(expectedBuildOnlyDiagnosticIds, result.Ids);
        }
 
        private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference()
        {
            var builder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<DiagnosticAnalyzer>>();
            builder.Add(LanguageNames.CSharp, ImmutableArray.Create(
                DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp),
                new BuildOnlyAnalyzer(),
                new LiveAnalyzer()));
            builder.Add(LanguageNames.VisualBasic, ImmutableArray.Create(
                DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.VisualBasic),
                new BuildOnlyAnalyzer(),
                new LiveAnalyzer()));
            return new(builder.ToImmutableDictionary());
        }
 
        private static string[] GetExpectedBuildOnlyDiagnosticIds(string languageName)
        {
            using var _ = ArrayBuilder<string>.GetInstance(out var builder);
 
            // NOTE: 'CSharpLspBuildOnlyDiagnosticsTests' and 'VisualBasicLspBuildOnlyDiagnosticsTests' already verify that
            // the corresponding build-only diagnostic providers return expected compiler build-only diagnostic IDs.
            // So, here we just directly append 'attribute.BuildOnlyDiagnostics' from these providers to our expected build-only diagnostic IDs.
            var compilerBuildOnlyDiagnosticsType = languageName switch
            {
                LanguageNames.CSharp => typeof(CSharp.LanguageServer.CSharpLspBuildOnlyDiagnostics),
                LanguageNames.VisualBasic => typeof(VisualBasic.LanguageServer.VisualBasicLspBuildOnlyDiagnostics),
                _ => null
            };
 
            if (compilerBuildOnlyDiagnosticsType != null)
            {
                var attribute = compilerBuildOnlyDiagnosticsType.GetCustomAttribute<LspBuildOnlyDiagnosticsAttribute>();
                builder.AddRange(attribute.BuildOnlyDiagnostics);
            }
 
            builder.Add(BuildOnlyAnalyzer.Id);
            return builder.ToArray();
        }
 
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        private sealed class BuildOnlyAnalyzer : DiagnosticAnalyzer
        {
            public const string Id = "BuildOnly0001";
            private static readonly DiagnosticDescriptor s_descriptor = new(Id, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, customTags: [WellKnownDiagnosticTags.CompilationEnd]);
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
 
            public override void Initialize(AnalysisContext context)
            {
            }
        }
 
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        private sealed class LiveAnalyzer : DiagnosticAnalyzer
        {
            public const string Id = "Live0001";
            private static readonly DiagnosticDescriptor s_descriptor = new(Id, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
 
            public override void Initialize(AnalysisContext context)
            {
            }
        }
    }
}