File: Diagnostics\TestDiagnosticAnalyzer.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// 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.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Diagnostics;
 
namespace Microsoft.CodeAnalysis.Test.Utilities
{
    public abstract class TestDiagnosticAnalyzer<TLanguageKindEnum> : DiagnosticAnalyzer where TLanguageKindEnum : struct
    {
        protected static readonly ImmutableArray<SymbolKind> AllSymbolKinds = GetAllEnumValues<SymbolKind>();
 
        protected static readonly ImmutableArray<TLanguageKindEnum> AllSyntaxKinds = GetAllEnumValues<TLanguageKindEnum>();
 
        protected static readonly ImmutableArray<string> AllAnalyzerMemberNames = new string[] { "AnalyzeCodeBlock", "AnalyzeCompilation", "AnalyzeNode", "AnalyzeSemanticModel", "AnalyzeSymbol", "AnalyzeSyntaxTree", "AnalyzeAdditionalFile", "Initialize", "SupportedDiagnostics" }.ToImmutableArray();
 
        protected static readonly DiagnosticDescriptor DefaultDiagnostic =
#pragma warning disable RS1029 // Do not use reserved diagnostic IDs.
            new DiagnosticDescriptor("CA7777", "CA7777_AnalyzerTestDiagnostic", "I'm here for test purposes", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true);
#pragma warning restore RS1029 // Do not use reserved diagnostic IDs.
 
        private static ImmutableArray<T> GetAllEnumValues<T>()
        {
            return ImmutableArray.CreateRange(Enum.GetValues(typeof(T)).Cast<T>());
        }
 
        protected abstract void OnAbstractMember(string abstractMemberName, SyntaxNode node = null, ISymbol symbol = null, [CallerMemberName] string callerName = null);
        protected virtual void OnOptions(AnalyzerOptions options, [CallerMemberName] string callerName = null) { }
 
        #region Implementation
 
        public override void Initialize(AnalysisContext context)
        {
            OnAbstractMember("Initialize");
 
            context.RegisterCodeBlockStartAction<TLanguageKindEnum>(new NestedCodeBlockAnalyzer(this).Initialize);
 
            context.RegisterCompilationAction(this.AnalyzeCompilation);
            context.RegisterSemanticModelAction(this.AnalyzeSemanticModel);
            context.RegisterCodeBlockAction(this.AnalyzeCodeBlock);
            context.RegisterSymbolAction(this.AnalyzeSymbol, AllSymbolKinds.ToArray());
            context.RegisterSyntaxTreeAction(this.AnalyzeSyntaxTree);
            context.RegisterAdditionalFileAction(this.AnalyzeAdditionalFile);
            context.RegisterSyntaxNodeAction<TLanguageKindEnum>(this.AnalyzeNode, AllSyntaxKinds.ToArray());
        }
 
        private void AnalyzeCodeBlock(CodeBlockAnalysisContext context)
        {
            OnAbstractMember("CodeBlock", context.CodeBlock, context.OwningSymbol);
            OnOptions(context.Options);
        }
 
        private void AnalyzeCompilation(CompilationAnalysisContext context)
        {
            OnAbstractMember("Compilation");
            OnOptions(context.Options);
        }
 
        private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
        {
            OnAbstractMember("SemanticModel");
            OnOptions(context.Options);
        }
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get
            {
                OnAbstractMember("SupportedDiagnostics");
                return ImmutableArray.Create(DefaultDiagnostic);
            }
        }
 
        private void AnalyzeSymbol(SymbolAnalysisContext context)
        {
            OnAbstractMember("Symbol", symbol: context.Symbol);
            OnOptions(context.Options);
        }
 
        private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context)
        {
            OnAbstractMember("SyntaxTree");
            OnOptions(context.Options);
        }
 
        private void AnalyzeAdditionalFile(AdditionalFileAnalysisContext context)
        {
            OnAbstractMember("AdditionalFile");
            OnOptions(context.Options);
        }
 
        private void AnalyzeNode(SyntaxNodeAnalysisContext context)
        {
            OnAbstractMember("SyntaxNode", context.Node);
            OnOptions(context.Options);
        }
 
        private class NestedCodeBlockAnalyzer
        {
            private readonly TestDiagnosticAnalyzer<TLanguageKindEnum> _container;
 
            public NestedCodeBlockAnalyzer(TestDiagnosticAnalyzer<TLanguageKindEnum> container)
            {
                _container = container;
            }
 
            public void Initialize(CodeBlockStartAnalysisContext<TLanguageKindEnum> context)
            {
                _container.OnAbstractMember("CodeBlockStart", context.CodeBlock, context.OwningSymbol);
                context.RegisterCodeBlockEndAction(_container.AnalyzeCodeBlock);
                context.RegisterSyntaxNodeAction(_container.AnalyzeNode, TestDiagnosticAnalyzer<TLanguageKindEnum>.AllSyntaxKinds.ToArray());
            }
        }
 
        #endregion
    }
}