// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis
{
    public static class CommonDiagnosticAnalyzers
    {
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class AnalyzerForErrorLogTest : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor1 = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true,
                description: "Description1",
                helpLinkUri: "HelpLink1",
                customTags: new[] { "1_CustomTag1", "1_CustomTag2" });

            public static readonly DiagnosticDescriptor Descriptor2 = new DiagnosticDescriptor(
                "ID2",
                "Title2",
                "Message2",
                "Category2",
                defaultSeverity: DiagnosticSeverity.Error,
                isEnabledByDefault: true,
                description: "Description2",
                helpLinkUri: "HelpLink2",
                customTags: new[] { "2_CustomTag1", "2_CustomTag2" });

            private static readonly ImmutableDictionary<string, string> s_properties =
                new Dictionary<string, string> { { "Key1", "Value1" }, { "Key2", "Value2" } }.ToImmutableDictionary();

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(Descriptor1, Descriptor2);
                }
            }

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(context =>
                {
                    // With location diagnostic.
                    context.RegisterSyntaxTreeAction(context =>
                    {
                        var location = context.Tree.GetRoot().GetLocation();
                        context.ReportDiagnostic(Diagnostic.Create(Descriptor1, location, s_properties));
                    });

                    // No location diagnostic.
                    context.RegisterCompilationEndAction(context =>
                        context.ReportDiagnostic(Diagnostic.Create(Descriptor2, Location.None, s_properties)));
                });
            }

            private static string GetExpectedPropertiesMapText()
            {
                var expectedText = @"
            ""customProperties"": {";

                foreach (var kvp in s_properties.OrderBy(kvp => kvp.Key))
                {
                    if (!expectedText.EndsWith("{"))
                    {
                        expectedText += ",";
                    }

                    expectedText += string.Format(@"
              ""{0}"": ""{1}""", kvp.Key, kvp.Value);
                }

                expectedText += @"
            }";

                return expectedText;
            }

            public static string GetExpectedV1ErrorLogResultsAndRulesText(Compilation compilation, bool warnAsError = false)
            {
                var tree = compilation.SyntaxTrees.First();
                var root = tree.GetRoot();
                var expectedLineSpan = root.GetLocation().GetLineSpan();
                var filePath = GetUriForPath(tree.FilePath);
                var effectiveSeverity1 = warnAsError || Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning";
                var effectiveSeverity2 = warnAsError || Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning";
                var warningLevelText = warnAsError ? string.Empty : @"
            ""warningLevel"": 1,";

                return @"
      ""results"": [
        {
          ""ruleId"": """ + Descriptor1.Id + @""",
          ""level"": """ + effectiveSeverity1 + @""",
          ""message"": """ + Descriptor1.MessageFormat + @""",
          ""locations"": [
            {
              ""resultFile"": {
                ""uri"": """ + filePath + @""",
                ""region"": {
                  ""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
                  ""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
                  ""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
                  ""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
                }
              }
            }
          ],
          ""properties"": {" +
             warningLevelText + GetExpectedPropertiesMapText() + @"
          }
        },
        {
          ""ruleId"": """ + Descriptor2.Id + @""",
          ""level"": """ + effectiveSeverity2 + @""",
          ""message"": """ + Descriptor2.MessageFormat + @""",
          ""properties"": {" +
             GetExpectedPropertiesMapText() + @"
          }
        }
      ],
      ""rules"": {
        """ + Descriptor1.Id + @""": {
          ""id"": """ + Descriptor1.Id + @""",
          ""shortDescription"": """ + Descriptor1.Title + @""",
          ""fullDescription"": """ + Descriptor1.Description + @""",
          ""defaultLevel"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""helpUri"": """ + Descriptor1.HelpLinkUri + @""",
          ""properties"": {
            ""category"": """ + Descriptor1.Category + @""",
            ""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
            ""tags"": [
              " + String.Join("," + Environment.NewLine + "              ", Descriptor1.CustomTags.Select(s => $"\"{s}\"")) + @"
            ]
          }
        },
        """ + Descriptor2.Id + @""": {
          ""id"": """ + Descriptor2.Id + @""",
          ""shortDescription"": """ + Descriptor2.Title + @""",
          ""fullDescription"": """ + Descriptor2.Description + @""",
          ""defaultLevel"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""helpUri"": """ + Descriptor2.HelpLinkUri + @""",
          ""properties"": {
            ""category"": """ + Descriptor2.Category + @""",
            ""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
            ""tags"": [
              " + String.Join("," + Environment.NewLine + "              ", Descriptor2.CustomTags.Select(s => $"\"{s}\"")) + @"
            ]
          }
        }
      }
    }
  ]
}";
            }

            public static string GetExpectedV1ErrorLogWithSuppressionResultsAndRulesText(Compilation compilation)
            {
                var tree = compilation.SyntaxTrees.First();
                var root = tree.GetRoot();
                var expectedLineSpan = root.GetLocation().GetLineSpan();
                var filePath = GetUriForPath(tree.FilePath);

                return @"
      ""results"": [
        {
          ""ruleId"": """ + Descriptor1.Id + @""",
          ""level"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""message"": """ + Descriptor1.MessageFormat + @""",
          ""suppressionStates"": [
            ""suppressedInSource""
          ],
          ""locations"": [
            {
              ""resultFile"": {
                ""uri"": """ + filePath + @""",
                ""region"": {
                  ""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
                  ""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
                  ""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
                  ""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
                }
              }
            }
          ],
          ""properties"": {
            ""warningLevel"": 1," + GetExpectedPropertiesMapText() + @"
          }
        },
        {
          ""ruleId"": """ + Descriptor2.Id + @""",
          ""level"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""message"": """ + Descriptor2.MessageFormat + @""",
          ""properties"": {" +
             GetExpectedPropertiesMapText() + @"
          }
        }
      ],
      ""rules"": {
        """ + Descriptor1.Id + @""": {
          ""id"": """ + Descriptor1.Id + @""",
          ""shortDescription"": """ + Descriptor1.Title + @""",
          ""fullDescription"": """ + Descriptor1.Description + @""",
          ""defaultLevel"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""helpUri"": """ + Descriptor1.HelpLinkUri + @""",
          ""properties"": {
            ""category"": """ + Descriptor1.Category + @""",
            ""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
            ""tags"": [
              " + String.Join("," + Environment.NewLine + "              ", Descriptor1.CustomTags.Select(s => $"\"{s}\"")) + @"
            ]
          }
        },
        """ + Descriptor2.Id + @""": {
          ""id"": """ + Descriptor2.Id + @""",
          ""shortDescription"": """ + Descriptor2.Title + @""",
          ""fullDescription"": """ + Descriptor2.Description + @""",
          ""defaultLevel"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""helpUri"": """ + Descriptor2.HelpLinkUri + @""",
          ""properties"": {
            ""category"": """ + Descriptor2.Category + @""",
            ""isEnabledByDefault"": " + (Descriptor2.IsEnabledByDefault ? "true" : "false") + @",
            ""tags"": [
              " + String.Join("," + Environment.NewLine + "              ", Descriptor2.CustomTags.Select(s => $"\"{s}\"")) + @"
            ]
          }
        }
      }
    }
  ]
}";
            }

            public static string GetExpectedV2ErrorLogResultsText(Compilation compilation, bool warnAsError = false)
            {
                var tree = compilation.SyntaxTrees.First();
                var root = tree.GetRoot();
                var expectedLineSpan = root.GetLocation().GetLineSpan();
                var filePath = GetUriForPath(tree.FilePath);
                var effectiveSeverity1 = warnAsError || Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning";
                var effectiveSeverity2 = warnAsError || Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning";
                var warningLevelText = warnAsError ? string.Empty : @"
            ""warningLevel"": 1,";

                return
@"      ""results"": [
        {
          ""ruleId"": """ + Descriptor1.Id + @""",
          ""ruleIndex"": 0,
          ""level"": """ + effectiveSeverity1 + @""",
          ""message"": {
            ""text"": """ + Descriptor1.MessageFormat + @"""
          },
          ""locations"": [
            {
              ""physicalLocation"": {
                ""artifactLocation"": {
                  ""uri"": """ + filePath + @"""
                },
                ""region"": {
                  ""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
                  ""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
                  ""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
                  ""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
                }
              }
            }
          ],
          ""properties"": {" +
             warningLevelText + GetExpectedPropertiesMapText() + @"
          }
        },
        {
          ""ruleId"": """ + Descriptor2.Id + @""",
          ""ruleIndex"": 1,
          ""level"": """ + effectiveSeverity2 + @""",
          ""message"": {
            ""text"": """ + Descriptor2.MessageFormat + @"""
          },
          ""properties"": {" +
             GetExpectedPropertiesMapText() + @"
          }
        }
      ]";
            }

            public static string GetExpectedV2ErrorLogWithSuppressionResultsText(Compilation compilation, string justification, string suppressionType)
            {
                var tree = compilation.SyntaxTrees.First();
                var root = tree.GetRoot();
                var expectedLineSpan = root.GetLocation().GetLineSpan();
                var filePath = GetUriForPath(tree.FilePath);
                var expectedSuppressionPropertyMap = @",
              ""properties"": {
                ""suppressionType"": """ + suppressionType + @"""
              }";
                return
@"      ""results"": [
        {
          ""ruleId"": """ + Descriptor1.Id + @""",
          ""ruleIndex"": 0,
          ""level"": """ + (Descriptor1.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""message"": {
            ""text"": """ + Descriptor1.MessageFormat + @"""
          },
          ""suppressions"": [
            {
              ""kind"": ""inSource""" + (justification == null ? "" : @",
              ""justification"": """ + (justification) + @"""") + (suppressionType == null ? "" : expectedSuppressionPropertyMap) + @"
            }
          ],
          ""locations"": [
            {
              ""physicalLocation"": {
                ""artifactLocation"": {
                  ""uri"": """ + filePath + @"""
                },
                ""region"": {
                  ""startLine"": " + (expectedLineSpan.StartLinePosition.Line + 1) + @",
                  ""startColumn"": " + (expectedLineSpan.StartLinePosition.Character + 1) + @",
                  ""endLine"": " + (expectedLineSpan.EndLinePosition.Line + 1) + @",
                  ""endColumn"": " + (expectedLineSpan.EndLinePosition.Character + 1) + @"
                }
              }
            }
          ],
          ""properties"": {
            ""warningLevel"": 1," + GetExpectedPropertiesMapText() + @"
          }
        },
        {
          ""ruleId"": """ + Descriptor2.Id + @""",
          ""ruleIndex"": 1,
          ""level"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @""",
          ""message"": {
            ""text"": """ + Descriptor2.MessageFormat + @"""
          },
          ""properties"": {" +
             GetExpectedPropertiesMapText() + @"
          }
        }
      ]";
            }

            public static string GetExpectedV2SuppressionTextForRulesSection(string[] suppressionKinds)
            {
                if (suppressionKinds?.Length > 0)
                {
                    return @",
                ""isEverSuppressed"": ""true"",
                ""suppressionKinds"": [
                  " + string.Join("," + Environment.NewLine + "                  ", suppressionKinds.Select(s => $"\"{s}\"")) + @"
                ]";
                }

                return string.Empty;
            }

            public static string GetExpectedV2ErrorLogInvocationsText(
                params (string DescriptorId, int DescriptorIndex, ImmutableHashSet<ReportDiagnostic> EffectiveSeverities)[] overriddenEffectiveSeveritiesWithIndex)
            {
                if (overriddenEffectiveSeveritiesWithIndex.Length == 0)
                {
                    return string.Empty;
                }

                var first = true;
                var overridesContent = string.Empty;
                foreach (var (id, index, effectiveSeverities) in overriddenEffectiveSeveritiesWithIndex)
                {
                    foreach (var effectiveSeverity in effectiveSeverities.OrderBy(Comparer<ReportDiagnostic>.Default))
                    {
                        if (first)
                        {
                            first = false;
                        }
                        else
                        {
                            overridesContent += ",";
                        }

                        overridesContent += $@"
            {{
              ""descriptor"": {{
                ""id"": ""{id}"",
                ""index"": {index}
              }},
              ""configuration"": {{
                {GetConfigurationPropertyString(effectiveSeverity)}
              }}
            }}";
                    }
                }

                return $@"""invocations"": [
        {{
          ""executionSuccessful"": true,
          ""ruleConfigurationOverrides"": [{overridesContent}
          ]
        }}
      ]";
            }

            private static string GetConfigurationPropertyString(ReportDiagnostic severity)
            {
                if (severity == ReportDiagnostic.Suppress)
                    return @"""enabled"": false";

                var severityString = severity switch
                {
                    ReportDiagnostic.Error => "error",
                    ReportDiagnostic.Warn => "warning",
                    ReportDiagnostic.Info or ReportDiagnostic.Hidden => "note",
                    _ => throw ExceptionUtilities.UnexpectedValue(severity)
                };

                return $@"""level"": ""{severityString}""";
            }

            internal static string GetExpectedV2ErrorLogRulesText(
                ImmutableArray<(DiagnosticDescriptor Descriptor, DiagnosticDescriptorErrorLoggerInfo Info)> descriptorsWithInfo,
                CultureInfo culture,
                string[] suppressionKinds1 = null,
                string[] suppressionKinds2 = null)
            {
                var descriptor1Info = descriptorsWithInfo.Single(d => d.Descriptor.Id == Descriptor1.Id).Info;
                var descriptor1ExecutionTime = ReportAnalyzerUtil.GetFormattedAnalyzerExecutionTime(descriptor1Info.ExecutionTime, culture).Trim();
                var descriptor1ExecutionPercentage = ReportAnalyzerUtil.GetFormattedAnalyzerExecutionPercentage(descriptor1Info.ExecutionPercentage, culture).Trim();

                var descriptor2Info = descriptorsWithInfo.Single(d => d.Descriptor.Id == Descriptor2.Id).Info;
                var descriptor2ExecutionTime = ReportAnalyzerUtil.GetFormattedAnalyzerExecutionTime(descriptor2Info.ExecutionTime, culture).Trim();
                var descriptor2ExecutionPercentage = ReportAnalyzerUtil.GetFormattedAnalyzerExecutionPercentage(descriptor2Info.ExecutionPercentage, culture).Trim();

                return
@"          ""rules"": [
            {
              ""id"": """ + Descriptor1.Id + @""",
              ""shortDescription"": {
                ""text"": """ + Descriptor1.Title + @"""
              },
              ""fullDescription"": {
                ""text"": """ + Descriptor1.Description + @"""
              },
              ""helpUri"": """ + Descriptor1.HelpLinkUri + @""",
              ""properties"": {
                ""category"": """ + Descriptor1.Category + @"""" + GetExpectedV2SuppressionTextForRulesSection(suppressionKinds1) + @",
                ""executionTimeInSeconds"": """ + descriptor1ExecutionTime + @""",
                ""executionTimeInPercentage"": """ + descriptor1ExecutionPercentage + @""",
                ""tags"": [
                  " + string.Join("," + Environment.NewLine + "                  ", Descriptor1.CustomTags.Select(s => $"\"{s}\"")) + @"
                ]
              }
            },
            {
              ""id"": """ + Descriptor2.Id + @""",
              ""shortDescription"": {
                ""text"": """ + Descriptor2.Title + @"""
              },
              ""fullDescription"": {
                ""text"": """ + Descriptor2.Description + @"""
              },
              ""defaultConfiguration"": {
                ""level"": """ + (Descriptor2.DefaultSeverity == DiagnosticSeverity.Error ? "error" : "warning") + @"""
              },
              ""helpUri"": """ + Descriptor2.HelpLinkUri + @""",
              ""properties"": {
                ""category"": """ + Descriptor2.Category + @"""" + GetExpectedV2SuppressionTextForRulesSection(suppressionKinds2) + @",
                ""executionTimeInSeconds"": """ + descriptor2ExecutionTime + @""",
                ""executionTimeInPercentage"": """ + descriptor2ExecutionPercentage + @""",
                ""tags"": [
                  " + String.Join("," + Environment.NewLine + "                  ", Descriptor2.CustomTags.Select(s => $"\"{s}\"")) + @"
                ]
              }
            }
          ]";
            }

            public static string GetUriForPath(string path)
            {
                var uri = new Uri(path, UriKind.RelativeOrAbsolute);
                return uri.IsAbsoluteUri
                    ? uri.AbsoluteUri
                    : WebUtility.UrlEncode(uri.ToString());
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class SuppressorForErrorLogTest : DiagnosticSuppressor
        {
            public static readonly SuppressionDescriptor Descriptor1 = new("SPR0001", AnalyzerForErrorLogTest.Descriptor1.Id, "SuppressorJustification1");
            public static readonly SuppressionDescriptor Descriptor2 = new("SPR0002", AnalyzerForErrorLogTest.Descriptor2.Id, "SuppressorJustification2");

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(Descriptor1, Descriptor2);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                foreach (var diagnostic in context.ReportedDiagnostics)
                {
                    var descriptor = diagnostic.Id == Descriptor1.SuppressedDiagnosticId ? Descriptor1 : Descriptor2;
                    context.ReportSuppression(Suppression.Create(descriptor, diagnostic));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class NotConfigurableDiagnosticAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor EnabledRule = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true,
                customTags: WellKnownDiagnosticTags.NotConfigurable);

            public static readonly DiagnosticDescriptor DisabledRule = new DiagnosticDescriptor(
                "ID2",
                "Title2",
                "Message2",
                "Category2",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: false,
                customTags: WellKnownDiagnosticTags.NotConfigurable);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(EnabledRule, DisabledRule);
                }
            }

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(compilationContext =>
                {
                    // Report enabled diagnostic.
                    compilationContext.ReportDiagnostic(Diagnostic.Create(EnabledRule, Location.None));

                    // Try to report disabled diagnostic.
                    compilationContext.ReportDiagnostic(Diagnostic.Create(DisabledRule, Location.None));
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class CodeBlockActionAnalyzer : DiagnosticAnalyzer
        {
            private readonly bool _onlyStatelessAction;

            public CodeBlockActionAnalyzer(bool onlyStatelessAction = false)
            {
                _onlyStatelessAction = onlyStatelessAction;
            }

            public static readonly DiagnosticDescriptor CodeBlockTopLevelRule = new DiagnosticDescriptor(
                "CodeBlockTopLevelRuleId",
                "CodeBlockTopLevelRuleTitle",
                "CodeBlock : {0}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor CodeBlockPerCompilationRule = new DiagnosticDescriptor(
                "CodeBlockPerCompilationRuleId",
                "CodeBlockPerCompilationRuleTitle",
                "CodeBlock : {0}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(CodeBlockTopLevelRule, CodeBlockPerCompilationRule);
                }
            }

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCodeBlockAction(codeBlockContext =>
                {
                    codeBlockContext.ReportDiagnostic(Diagnostic.Create(CodeBlockTopLevelRule, codeBlockContext.OwningSymbol.Locations[0], codeBlockContext.OwningSymbol.Name));
                });

                if (!_onlyStatelessAction)
                {
                    context.RegisterCompilationStartAction(compilationStartContext =>
                    {
                        compilationStartContext.RegisterCodeBlockAction(codeBlockContext =>
                        {
                            codeBlockContext.ReportDiagnostic(Diagnostic.Create(CodeBlockPerCompilationRule, codeBlockContext.OwningSymbol.Locations[0], codeBlockContext.OwningSymbol.Name));
                        });
                    });
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public class CSharpCodeBlockObjectCreationAnalyzer : CodeBlockObjectCreationAnalyzer<SyntaxKind>
        {
            protected override SyntaxKind ObjectCreationExpressionKind => SyntaxKind.ObjectCreationExpression;
        }

        [DiagnosticAnalyzer(LanguageNames.VisualBasic)]
        public class VisualBasicCodeBlockObjectCreationAnalyzer : CodeBlockObjectCreationAnalyzer<VisualBasic.SyntaxKind>
        {
            protected override VisualBasic.SyntaxKind ObjectCreationExpressionKind => VisualBasic.SyntaxKind.ObjectCreationExpression;
        }

        public abstract class CodeBlockObjectCreationAnalyzer<TLanguageKindEnum> : DiagnosticAnalyzer
            where TLanguageKindEnum : struct
        {
            public static readonly DiagnosticDescriptor DiagnosticDescriptor = new DiagnosticDescriptor(
                "Id",
                "Title",
                "Message",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptor);
            protected abstract TLanguageKindEnum ObjectCreationExpressionKind { get; }

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCodeBlockStartAction<TLanguageKindEnum>(codeBlockStartContext =>
                {
                    codeBlockStartContext.RegisterSyntaxNodeAction(syntaxNodeContext =>
                    {
                        syntaxNodeContext.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptor, syntaxNodeContext.Node.GetLocation()));
                    },
                    ObjectCreationExpressionKind);
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public class CSharpGenericNameAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = nameof(DiagnosticId);
            public const string Title = nameof(Title);
            public const string Message = nameof(Message);
            public const string Category = nameof(Category);
            public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning;

            internal static DiagnosticDescriptor Rule =
                new DiagnosticDescriptor(DiagnosticId, Title, Message,
                                         Category, Severity, isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
                 ImmutableArray.Create(Rule);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.GenericName);
            }

            private void AnalyzeNode(SyntaxNodeAnalysisContext context)
            {
                context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public class CSharpNamespaceDeclarationAnalyzer : AbstractNamespaceDeclarationAnalyzer<SyntaxKind>
        {
            protected override SyntaxKind NamespaceDeclarationSyntaxKind => SyntaxKind.NamespaceDeclaration;
        }

        [DiagnosticAnalyzer(LanguageNames.VisualBasic)]
        public class VisualBasicNamespaceDeclarationAnalyzer : AbstractNamespaceDeclarationAnalyzer<VisualBasic.SyntaxKind>
        {
            protected override VisualBasic.SyntaxKind NamespaceDeclarationSyntaxKind => VisualBasic.SyntaxKind.NamespaceStatement;
        }

        public abstract class AbstractNamespaceDeclarationAnalyzer<TLanguageKindEnum> : DiagnosticAnalyzer
            where TLanguageKindEnum : struct
        {
            public const string DiagnosticId = nameof(DiagnosticId);
            public const string Title = nameof(Title);
            public const string Message = nameof(Message);
            public const string Category = nameof(Category);
            public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning;

            internal static DiagnosticDescriptor Rule =
                new DiagnosticDescriptor(DiagnosticId, Title, Message,
                                         Category, Severity, isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
                 ImmutableArray.Create(Rule);
            protected abstract TLanguageKindEnum NamespaceDeclarationSyntaxKind { get; }

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxNodeAction(AnalyzeNode, NamespaceDeclarationSyntaxKind);
            }

            private void AnalyzeNode(SyntaxNodeAnalysisContext context)
            {
                context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithNoActions : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor DummyRule = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DummyRule);
            public override void Initialize(AnalysisContext context) { }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithDisabledRules : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: false);

            internal readonly ConcurrentSet<ISymbol> CallbackSymbols = new();

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(context =>
                {
                    CallbackSymbols.Add(context.Symbol);
                    context.ReportDiagnostic(Diagnostic.Create(Rule, context.Symbol.Locations[0]));
                }, SymbolKind.NamedType);
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class EnsureNoMergedNamespaceSymbolAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = nameof(DiagnosticId);
            public const string Title = nameof(Title);
            public const string Message = nameof(Message);
            public const string Category = nameof(Category);
            public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning;

            internal static DiagnosticDescriptor Rule =
                new DiagnosticDescriptor(DiagnosticId, Title, Message,
                                         Category, Severity, isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
                 ImmutableArray.Create(Rule);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Namespace);
            }

            private void AnalyzeSymbol(SymbolAnalysisContext context)
            {
                // Ensure we are not invoked for merged namespace symbol, but instead for constituent namespace scoped to the source assembly.
                var ns = (INamespaceSymbol)context.Symbol;
                if (ns.ContainingAssembly != context.Compilation.Assembly || ns.ConstituentNamespaces.Length > 1)
                {
                    context.ReportDiagnostic(Diagnostic.Create(Rule, ns.Locations[0]));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithNoSupportedDiagnostics : DiagnosticAnalyzer
        {
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
            public override void Initialize(AnalysisContext context) { }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithInvalidDiagnosticId : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "Invalid ID",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(compilationContext =>
                    compilationContext.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.None)));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithNullDescriptor : DiagnosticAnalyzer
        {
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create((DiagnosticDescriptor)null);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(_ => { });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithCSharpCompilerDiagnosticId : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
#pragma warning disable RS1029 // Do not use reserved diagnostic IDs.
                "CS101",
#pragma warning restore RS1029 // Do not use reserved diagnostic IDs.
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(compilationContext =>
                    compilationContext.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.None)));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithBasicCompilerDiagnosticId : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
#pragma warning disable RS1029 // Do not use reserved diagnostic IDs.
                "BC101",
#pragma warning restore RS1029 // Do not use reserved diagnostic IDs.
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(compilationContext =>
                    compilationContext.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.None)));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithInvalidDiagnosticSpan : DiagnosticAnalyzer
        {
            private readonly TextSpan _badSpan;

            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title1",
                "Message",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public AnalyzerWithInvalidDiagnosticSpan(TextSpan badSpan) => _badSpan = badSpan;
            public Exception ThrownException { get; set; }
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxTreeAction(c =>
                {
                    try
                    {
                        ThrownException = null;
                        c.ReportDiagnostic(Diagnostic.Create(Descriptor, SourceLocation.Create(c.Tree, _badSpan)));
                    }
                    catch (Exception e)
                    {
                        ThrownException = e;
                        throw;
                    }
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithInvalidDiagnosticLocation : DiagnosticAnalyzer
        {
            private readonly Location _invalidLocation;
            private readonly ActionKind _actionKind;
            private readonly bool _testInvalidAdditionalLocation;

            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title1",
                "Message {0}",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public enum ActionKind
            {
                Symbol,
                CodeBlock,
                Operation,
                OperationBlockEnd,
                Compilation,
                CompilationEnd,
                SyntaxTree
            }

            public AnalyzerWithInvalidDiagnosticLocation(SyntaxTree treeInAnotherCompilation, ActionKind actionKind, bool testInvalidAdditionalLocation)
            {
                _invalidLocation = treeInAnotherCompilation.GetRoot().GetLocation();
                _actionKind = actionKind;
                _testInvalidAdditionalLocation = testInvalidAdditionalLocation;
            }

            private void ReportDiagnostic(Action<Diagnostic> addDiagnostic, ActionKind actionKindBeingRun)
            {
                if (_actionKind == actionKindBeingRun)
                {
                    var diagnostic = _testInvalidAdditionalLocation ?
                        Diagnostic.Create(Descriptor, Location.None, additionalLocations: new[] { _invalidLocation }) :
                        Diagnostic.Create(Descriptor, _invalidLocation);
                    addDiagnostic(diagnostic);
                }
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(cc =>
                {
                    cc.RegisterSymbolAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.Symbol), SymbolKind.NamedType);
                    cc.RegisterCodeBlockAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.CodeBlock));
                    cc.RegisterCompilationEndAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.CompilationEnd));

                    cc.RegisterOperationBlockStartAction(oc =>
                    {
                        oc.RegisterOperationAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.Operation), OperationKind.VariableDeclarationGroup);
                        oc.RegisterOperationBlockEndAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.OperationBlockEnd));
                    });
                });

                context.RegisterSyntaxTreeAction(c => ReportDiagnostic(c.ReportDiagnostic, ActionKind.SyntaxTree));
                context.RegisterCompilationAction(cc => ReportDiagnostic(cc.ReportDiagnostic, ActionKind.Compilation));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerThatThrowsInSupportedDiagnostics : DiagnosticAnalyzer
        {
            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => throw new NotImplementedException();
            public override void Initialize(AnalysisContext context) { }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerThatThrowsInGetMessage : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                new MyLocalizableStringThatThrows(),
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(symbolContext =>
                {
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Symbol.Locations[0]));
                }, SymbolKind.NamedType);
            }

            private sealed class MyLocalizableStringThatThrows : LocalizableString
            {
                protected override bool AreEqual(object other)
                {
                    return ReferenceEquals(this, other);
                }

                protected override int GetHash()
                {
                    return 0;
                }

                protected override string GetText(IFormatProvider formatProvider)
                {
                    throw new NotImplementedException();
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerReportingMisformattedDiagnostic : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Symbol Name: {0}, Extra argument: {1}",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(symbolContext =>
                {
                    // Report diagnostic with incorrect number of message format arguments.
                    symbolContext.ReportDiagnostic(Diagnostic.Create(Rule, symbolContext.Symbol.Locations[0], symbolContext.Symbol.Name));
                }, SymbolKind.NamedType);
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class CompilationAnalyzerWithSeverity : DiagnosticAnalyzer
        {
            public const string DiagnosticId = "ID1000";
            public CompilationAnalyzerWithSeverity(
                DiagnosticSeverity severity,
                bool configurable)
            {
                var customTags = !configurable ? new[] { WellKnownDiagnosticTags.NotConfigurable } : Array.Empty<string>();
                Descriptor = new DiagnosticDescriptor(
                    DiagnosticId,
                    "Description1",
                    string.Empty,
                    "Analysis",
                    severity,
                    true,
                    customTags: customTags);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(this.OnCompilation);
            }

            private void OnCompilation(CompilationAnalysisContext context)
            {
                // Report the diagnostic on all trees in compilation.
                foreach (var tree in context.Compilation.SyntaxTrees)
                {
                    context.ReportDiagnostic(Diagnostic.Create(Descriptor, tree.GetRoot().GetLocation()));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class SemanticModelAnalyzerWithId : DiagnosticAnalyzer
        {
            public SemanticModelAnalyzerWithId(string diagnosticId)
            {
                Descriptor = new DiagnosticDescriptor(
                    diagnosticId,
                    "Description1",
                    string.Empty,
                    "Analysis",
                    DiagnosticSeverity.Warning,
                    isEnabledByDefault: true);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);

            public override void Initialize(AnalysisContext context) =>
                context.RegisterSemanticModelAction(context =>
                    context.ReportDiagnostic(
                        Diagnostic.Create(Descriptor, context.SemanticModel.SyntaxTree.GetRoot().GetLocation())));
        }

        /// <summary>
        /// This analyzer is intended to be used only when concurrent execution is enabled for analyzers.
        /// This analyzer will deadlock if the driver runs analyzers on a single thread OR takes a lock around callbacks into this analyzer to prevent concurrent analyzer execution
        /// Former indicates a bug in the test using this analyzer and the latter indicates a bug in the analyzer driver.
        /// </summary>
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class ConcurrentAnalyzer : DiagnosticAnalyzer
        {
            private readonly ImmutableHashSet<string> _symbolNames;
            private int _token;

            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ConcurrentAnalyzerId",
                "Title",
                "ConcurrentAnalyzerMessage for symbol '{0}'",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public ConcurrentAnalyzer(IEnumerable<string> symbolNames)
            {
                Assert.True(Environment.ProcessorCount > 1, "This analyzer is intended to be used only in a concurrent environment.");
                _symbolNames = symbolNames.ToImmutableHashSet();
                _token = 0;
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(this.OnCompilationStart);

                // Enable concurrent action callbacks on analyzer.
                context.EnableConcurrentExecution();
            }

            private void OnCompilationStart(CompilationStartAnalysisContext context)
            {
                Assert.True(context.Compilation.Options.ConcurrentBuild, "This analyzer is intended to be used only when concurrent build is enabled.");

                var pendingSymbols = new ConcurrentSet<INamedTypeSymbol>();
                foreach (var type in context.Compilation.GlobalNamespace.GetTypeMembers())
                {
                    if (_symbolNames.Contains(type.Name))
                    {
                        pendingSymbols.Add(type);
                    }
                }

                context.RegisterSymbolAction(symbolContext =>
                {
                    if (!pendingSymbols.Remove((INamedTypeSymbol)symbolContext.Symbol))
                    {
                        return;
                    }

                    var myToken = Interlocked.Increment(ref _token);
                    if (myToken == 1)
                    {
                        // Wait for all symbol callbacks to execute.
                        // This analyzer will deadlock if the driver doesn't attempt concurrent callbacks.
                        while (pendingSymbols.Any())
                        {
                            Thread.Sleep(10);
                        }
                    }

                    // ok, now report diagnostic on the symbol.
                    var diagnostic = Diagnostic.Create(Descriptor, symbolContext.Symbol.Locations[0], symbolContext.Symbol.Name);
                    symbolContext.ReportDiagnostic(diagnostic);
                }, SymbolKind.NamedType);
            }
        }

        /// <summary>
        /// This analyzer will report diagnostics only if it receives any concurrent action callbacks, which would be a
        /// bug in the analyzer driver as this analyzer doesn't invoke <see cref="AnalysisContext.EnableConcurrentExecution"/>.
        /// </summary>
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class NonConcurrentAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "NonConcurrentAnalyzerId",
                "Title",
                "Analyzer driver made concurrent action callbacks, when analyzer didn't register for concurrent execution",
                "Category",
                DiagnosticSeverity.Warning,
                true);
            private const int registeredActionCounts = 1000;

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                SemaphoreSlim gate = new SemaphoreSlim(initialCount: registeredActionCounts);
                for (var i = 0; i < registeredActionCounts; i++)
                {
                    context.RegisterSymbolAction(symbolContext =>
                    {
                        using (gate.DisposableWait(symbolContext.CancellationToken))
                        {
                            ReportDiagnosticIfActionInvokedConcurrently(gate, symbolContext);
                        }
                    }, SymbolKind.NamedType);
                }
            }

            private void ReportDiagnosticIfActionInvokedConcurrently(SemaphoreSlim gate, SymbolAnalysisContext symbolContext)
            {
                if (gate.CurrentCount != registeredActionCounts - 1)
                {
                    var diagnostic = Diagnostic.Create(Descriptor, symbolContext.Symbol.Locations[0]);
                    symbolContext.ReportDiagnostic(diagnostic);
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class OperationAnalyzer : DiagnosticAnalyzer
        {
            private readonly ActionKind _actionKind;
            private readonly bool _verifyGetControlFlowGraph;
            private readonly ConcurrentDictionary<IOperation, (ControlFlowGraph Graph, ISymbol AssociatedSymbol)> _controlFlowGraphMapOpt;

            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title1",
                "{0} diagnostic",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public enum ActionKind
            {
                Operation,
                OperationInOperationBlockStart,
                OperationBlock,
                OperationBlockEnd
            }

            public OperationAnalyzer(ActionKind actionKind, bool verifyGetControlFlowGraph = false)
            {
                _actionKind = actionKind;
                _verifyGetControlFlowGraph = verifyGetControlFlowGraph;
                _controlFlowGraphMapOpt = verifyGetControlFlowGraph ? new ConcurrentDictionary<IOperation, (ControlFlowGraph, ISymbol)>() : null;
            }

            public ImmutableArray<(ControlFlowGraph Graph, ISymbol AssociatedSymbol)> GetControlFlowGraphs()
            {
                Assert.True(_verifyGetControlFlowGraph);
                return _controlFlowGraphMapOpt.Values.OrderBy(flowGraphAndSymbol => flowGraphAndSymbol.Graph.OriginalOperation.Syntax.SpanStart).ToImmutableArray();
            }

            private void ReportDiagnostic(Action<Diagnostic> addDiagnostic, Location location)
            {
                var diagnostic = Diagnostic.Create(Descriptor, location, _actionKind);
                addDiagnostic(diagnostic);
            }

            private void CacheAndVerifyControlFlowGraph(ImmutableArray<IOperation> operationBlocks, Func<IOperation, (ControlFlowGraph Graph, ISymbol AssociatedSymbol)> getControlFlowGraph)
            {
                if (_verifyGetControlFlowGraph)
                {
                    foreach (var operationBlock in operationBlocks)
                    {
                        var controlFlowGraphAndSymbol = getControlFlowGraph(operationBlock);
                        Assert.NotNull(controlFlowGraphAndSymbol);
                        Assert.Same(operationBlock.GetRootOperation(), controlFlowGraphAndSymbol.Graph.OriginalOperation);

                        _controlFlowGraphMapOpt.Add(controlFlowGraphAndSymbol.Graph.OriginalOperation, controlFlowGraphAndSymbol);

                        // Verify analyzer driver caches the flow graph.
                        Assert.Same(controlFlowGraphAndSymbol.Graph, getControlFlowGraph(operationBlock).Graph);

                        // Verify exceptions for invalid inputs.
                        try
                        {
                            _ = getControlFlowGraph(null);
                        }
                        catch (ArgumentNullException ex)
                        {
                            Assert.Equal(new ArgumentNullException("operationBlock").Message, ex.Message);
                        }

                        try
                        {
                            _ = getControlFlowGraph(operationBlock.Descendants().First());
                        }
                        catch (ArgumentException ex)
                        {
                            Assert.Equal(new ArgumentException(CodeAnalysisResources.InvalidOperationBlockForAnalysisContext, "operationBlock").Message, ex.Message);
                        }
                    }
                }
            }

            private void VerifyControlFlowGraph(OperationAnalysisContext operationContext, bool inBlockAnalysisContext)
            {
                if (_verifyGetControlFlowGraph)
                {
                    var controlFlowGraph = operationContext.GetControlFlowGraph();
                    Assert.NotNull(controlFlowGraph);

                    // Verify analyzer driver caches the flow graph.
                    Assert.Same(controlFlowGraph, operationContext.GetControlFlowGraph());

                    var rootOperation = operationContext.Operation.GetRootOperation();
                    if (inBlockAnalysisContext)
                    {
                        // Verify same flow graph returned from containing block analysis context.
                        Assert.Same(controlFlowGraph, _controlFlowGraphMapOpt[rootOperation].Graph);
                    }
                    else
                    {
                        _controlFlowGraphMapOpt[rootOperation] = (controlFlowGraph, operationContext.ContainingSymbol);
                    }
                }
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                switch (_actionKind)
                {
                    case ActionKind.OperationBlockEnd:
                        context.RegisterOperationBlockStartAction(blockStartContext =>
                        {
                            blockStartContext.RegisterOperationBlockEndAction(c => ReportDiagnostic(c.ReportDiagnostic, c.OwningSymbol.Locations[0]));
                            CacheAndVerifyControlFlowGraph(blockStartContext.OperationBlocks, op => (blockStartContext.GetControlFlowGraph(op), blockStartContext.OwningSymbol));
                        });

                        break;

                    case ActionKind.OperationBlock:
                        context.RegisterOperationBlockAction(blockContext =>
                        {
                            ReportDiagnostic(blockContext.ReportDiagnostic, blockContext.OwningSymbol.Locations[0]);
                            CacheAndVerifyControlFlowGraph(blockContext.OperationBlocks, op => (blockContext.GetControlFlowGraph(op), blockContext.OwningSymbol));
                        });

                        break;

                    case ActionKind.Operation:
                        context.RegisterOperationAction(operationContext =>
                        {
                            ReportDiagnostic(operationContext.ReportDiagnostic, operationContext.Operation.Syntax.GetLocation());
                            VerifyControlFlowGraph(operationContext, inBlockAnalysisContext: false);
                        }, OperationKind.Literal);

                        break;

                    case ActionKind.OperationInOperationBlockStart:
                        context.RegisterOperationBlockStartAction(blockContext =>
                        {
                            CacheAndVerifyControlFlowGraph(blockContext.OperationBlocks, op => (blockContext.GetControlFlowGraph(op), blockContext.OwningSymbol));
                            blockContext.RegisterOperationAction(operationContext =>
                            {
                                ReportDiagnostic(operationContext.ReportDiagnostic, operationContext.Operation.Syntax.GetLocation());
                                VerifyControlFlowGraph(operationContext, inBlockAnalysisContext: true);
                            }, OperationKind.Literal);
                        });
                        break;
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class OperationBlockAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title1",
                "OperationBlock for {0}: {1}",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterOperationBlockAction(c =>
                {
                    foreach (var operationRoot in c.OperationBlocks)
                    {
                        var diagnostic = Diagnostic.Create(Descriptor, c.OwningSymbol.Locations[0], c.OwningSymbol.Name, operationRoot.Kind);
                        c.ReportDiagnostic(diagnostic);
                    }
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class FieldReferenceOperationAnalyzer : DiagnosticAnalyzer
        {
            private readonly bool _doOperationBlockAnalysis;
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title",
                "Field {0} = {1}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public FieldReferenceOperationAnalyzer(bool doOperationBlockAnalysis)
            {
                _doOperationBlockAnalysis = doOperationBlockAnalysis;
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                if (_doOperationBlockAnalysis)
                {
                    context.RegisterOperationBlockAction(operationBlockAnalysisContext =>
                    {
                        foreach (var operationBlock in operationBlockAnalysisContext.OperationBlocks)
                        {
                            foreach (var operation in operationBlock.DescendantsAndSelf().OfType<IFieldReferenceOperation>())
                            {
                                AnalyzerFieldReferenceOperation(operation, operationBlockAnalysisContext.ReportDiagnostic);
                            }
                        }
                    });
                }
                else
                {
                    context.RegisterOperationAction(AnalyzerOperation, OperationKind.FieldReference);
                }
            }

            private static void AnalyzerOperation(OperationAnalysisContext operationAnalysisContext)
            {
                AnalyzerFieldReferenceOperation((IFieldReferenceOperation)operationAnalysisContext.Operation, operationAnalysisContext.ReportDiagnostic);
            }

            private static void AnalyzerFieldReferenceOperation(IFieldReferenceOperation operation, Action<Diagnostic> reportDiagnostic)
            {
                var diagnostic = Diagnostic.Create(Descriptor, operation.Syntax.GetLocation(), operation.Field.Name, operation.Field.ConstantValue);
                reportDiagnostic(diagnostic);
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class MethodOrConstructorBodyOperationAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
                "ID",
                "Title",
                "Method {0}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterOperationAction(operationContext =>
                {
                    var diagnostic = Diagnostic.Create(Descriptor, operationContext.Operation.Syntax.GetLocation(), operationContext.ContainingSymbol.Name);
                    operationContext.ReportDiagnostic(diagnostic);
                }, OperationKind.MethodBody, OperationKind.ConstructorBody);
            }
        }

        public abstract class AbstractGeneratedCodeAnalyzer<TSyntaxKind> : DiagnosticAnalyzer
            where TSyntaxKind : struct
        {
            private readonly GeneratedCodeAnalysisFlags? _generatedCodeAnalysisFlagsOpt;
            private readonly bool _testIsGeneratedCodeInCallbacks;

            public static readonly DiagnosticDescriptor Warning = new DiagnosticDescriptor(
                "GeneratedCodeAnalyzerWarning",
                "Title",
                "GeneratedCodeAnalyzerMessage for '{0}'",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public static readonly DiagnosticDescriptor Error = new DiagnosticDescriptor(
                "GeneratedCodeAnalyzerError",
                "Title",
                "GeneratedCodeAnalyzerMessage for '{0}'",
                "Category",
                DiagnosticSeverity.Error,
                true);

            public static readonly DiagnosticDescriptor Summary = new DiagnosticDescriptor(
                "GeneratedCodeAnalyzerSummary",
                "Title2",
                "GeneratedCodeAnalyzer received callbacks for: '{0}' types and '{1}' files",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public static readonly DiagnosticDescriptor Summary2 = new DiagnosticDescriptor(
                "GeneratedCodeAnalyzerSummary",
                "Title2",
                "GeneratedCodeAnalyzer received callbacks for: '{0}' types and '{1}' files and '{2}' additional IsGeneratedCode callbacks",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            protected AbstractGeneratedCodeAnalyzer(GeneratedCodeAnalysisFlags? generatedCodeAnalysisFlagsOpt, bool testIsGeneratedCodeInCallbacks)
            {
                _generatedCodeAnalysisFlagsOpt = generatedCodeAnalysisFlagsOpt;
                _testIsGeneratedCodeInCallbacks = testIsGeneratedCodeInCallbacks;
            }

            protected abstract TSyntaxKind ClassDeclarationSyntaxKind { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Warning, Error, Summary, Summary2);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(this.OnCompilationStart);

                if (_generatedCodeAnalysisFlagsOpt.HasValue)
                {
                    // Configure analysis on generated code.
                    context.ConfigureGeneratedCodeAnalysis(_generatedCodeAnalysisFlagsOpt.Value);
                }
            }

            private void OnCompilationStart(CompilationStartAnalysisContext context)
            {
                var sortedCallbackSymbolNames = new SortedSet<string>();
                var sortedCallbackTreePaths = new SortedSet<string>();
                context.RegisterSymbolAction(symbolContext =>
                {
                    sortedCallbackSymbolNames.Add($"{symbolContext.Symbol.Name}(IsGeneratedCode:{symbolContext.IsGeneratedCode})");
                    ReportSymbolDiagnostics(symbolContext.Symbol, symbolContext.ReportDiagnostic);
                }, SymbolKind.NamedType);

                context.RegisterSyntaxTreeAction(treeContext =>
                {
                    sortedCallbackTreePaths.Add($"{treeContext.Tree.FilePath}(IsGeneratedCode:{treeContext.IsGeneratedCode})");
                    ReportTreeDiagnostics(treeContext.Tree, treeContext.ReportDiagnostic);
                });

                var sortedCallbackSyntaxNodeNames = new SortedSet<string>();
                var sortedCallbackOperationNames = new SortedSet<string>();
                var sortedCallbackSemanticModelPaths = new SortedSet<string>();
                var sortedCallbackSymbolStartNames = new SortedSet<string>();
                var sortedCallbackSymbolEndNames = new SortedSet<string>();
                var sortedCallbackOperationBlockStartNames = new SortedSet<string>();
                var sortedCallbackOperationBlockEndNames = new SortedSet<string>();
                var sortedCallbackOperationBlockNames = new SortedSet<string>();
                var sortedCallbackCodeBlockStartNames = new SortedSet<string>();
                var sortedCallbackCodeBlockEndNames = new SortedSet<string>();
                var sortedCallbackCodeBlockNames = new SortedSet<string>();
                if (_testIsGeneratedCodeInCallbacks)
                {
                    // Test all remaining analysis contexts that expose "IsGeneratedCode" flag
                    context.RegisterSyntaxNodeAction(context =>
                        sortedCallbackSyntaxNodeNames.Add($"{context.ContainingSymbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"),
                        ImmutableArray.Create(ClassDeclarationSyntaxKind));

                    context.RegisterSemanticModelAction(context =>
                        sortedCallbackSemanticModelPaths.Add($"{context.SemanticModel.SyntaxTree.FilePath}(IsGeneratedCode:{context.IsGeneratedCode})"));

                    context.RegisterSymbolStartAction(context =>
                    {
                        sortedCallbackSymbolStartNames.Add($"{context.Symbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})");

                        context.RegisterOperationBlockStartAction(context =>
                        {
                            if (context.OwningSymbol.Kind != SymbolKind.Method ||
                                context.OperationBlocks.IsEmpty)
                            {
                                return;
                            }

                            sortedCallbackOperationBlockStartNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})");

                            context.RegisterOperationAction(context =>
                                sortedCallbackOperationNames.Add($"{context.ContainingSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"),
                                OperationKind.Invocation);

                            context.RegisterOperationBlockEndAction(context =>
                                sortedCallbackOperationBlockEndNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"));
                        });

                        context.RegisterOperationBlockAction(context =>
                        {
                            if (context.OwningSymbol.Kind != SymbolKind.Method ||
                                context.OperationBlocks.IsEmpty)
                            {
                                return;
                            }

                            sortedCallbackOperationBlockNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})");
                        });

                        context.RegisterSymbolEndAction(context =>
                            sortedCallbackSymbolEndNames.Add($"{context.Symbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"));
                    }, SymbolKind.NamedType);

                    context.RegisterCodeBlockStartAction<TSyntaxKind>(context =>
                    {
                        if (context.OwningSymbol.Kind != SymbolKind.Method)
                        {
                            return;
                        }

                        sortedCallbackCodeBlockStartNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})");

                        context.RegisterCodeBlockEndAction(context =>
                            sortedCallbackCodeBlockEndNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})"));
                    });

                    context.RegisterCodeBlockAction(context =>
                    {
                        if (context.OwningSymbol.Kind != SymbolKind.Method)
                        {
                            return;
                        }

                        sortedCallbackCodeBlockNames.Add($"{context.OwningSymbol.ContainingType.Name}(IsGeneratedCode:{context.IsGeneratedCode})");
                    });
                }

                context.RegisterCompilationEndAction(endContext =>
                {
                    var arg1 = sortedCallbackSymbolNames.Join(",");
                    var arg2 = sortedCallbackTreePaths.Join(",");
                    var args = new object[] { arg1, arg2 };
                    var rule = Summary;

                    if (_testIsGeneratedCodeInCallbacks)
                    {
                        var arg3 = sortedCallbackSyntaxNodeNames.Join(",") + ";" +
                            sortedCallbackOperationNames.Join(",") + ";" +
                            sortedCallbackSemanticModelPaths.Join(",") + ";" +
                            sortedCallbackSymbolStartNames.Join(",") + ";" +
                            sortedCallbackSymbolEndNames.Join(",") + ";" +
                            sortedCallbackOperationBlockStartNames.Join(",") + ";" +
                            sortedCallbackOperationBlockEndNames.Join(",") + ";" +
                            sortedCallbackOperationBlockNames.Join(",") + ";" +
                            sortedCallbackCodeBlockStartNames.Join(",") + ";" +
                            sortedCallbackCodeBlockEndNames.Join(",") + ";" +
                            sortedCallbackCodeBlockNames.Join(",");
                        args = new object[] { arg1, arg2, arg3 };
                        rule = Summary2;
                    }

                    // Summary diagnostics about received callbacks.
                    var diagnostic = Diagnostic.Create(rule, Location.None, args);
                    endContext.ReportDiagnostic(diagnostic);
                });
            }

            private void ReportSymbolDiagnostics(ISymbol symbol, Action<Diagnostic> addDiagnostic)
            {
                ReportDiagnosticsCore(addDiagnostic, symbol.Locations[0], symbol.Name);
            }

            private void ReportTreeDiagnostics(SyntaxTree tree, Action<Diagnostic> addDiagnostic)
            {
                ReportDiagnosticsCore(addDiagnostic, tree.GetRoot().GetLastToken().GetLocation(), tree.FilePath);
            }

            private void ReportDiagnosticsCore(Action<Diagnostic> addDiagnostic, Location location, params object[] messageArguments)
            {
                // warning diagnostic
                var diagnostic = Diagnostic.Create(Warning, location, messageArguments);
                addDiagnostic(diagnostic);

                // error diagnostic
                diagnostic = Diagnostic.Create(Error, location, messageArguments);
                addDiagnostic(diagnostic);
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class GeneratedCodeAnalyzer2 : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
                "GeneratedCodeAnalyzer2Warning",
                "Title",
                "GeneratedCodeAnalyzer2Message for '{0}'; Total types analyzed: '{1}'",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
            public override void Initialize(AnalysisContext context)
            {
                // Analyze but don't report diagnostics on generated code.
                context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze);

                context.RegisterCompilationStartAction(compilationStartContext =>
                {
                    var namedTypes = new HashSet<ISymbol>();
                    compilationStartContext.RegisterSymbolAction(symbolContext => namedTypes.Add(symbolContext.Symbol), SymbolKind.NamedType);

                    compilationStartContext.RegisterCompilationEndAction(compilationEndContext =>
                    {
                        foreach (var namedType in namedTypes)
                        {
                            var diagnostic = Diagnostic.Create(Rule, namedType.Locations[0], namedType.Name, namedTypes.Count);
                            compilationEndContext.ReportDiagnostic(diagnostic);
                        }
                    });
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class SharedStateAnalyzer : DiagnosticAnalyzer
        {
            private readonly SyntaxTreeValueProvider<bool> _treeValueProvider;
            private readonly HashSet<SyntaxTree> _treeCallbackSet;

            private readonly SourceTextValueProvider<int> _textValueProvider;
            private readonly HashSet<SourceText> _textCallbackSet;

            public static readonly DiagnosticDescriptor GeneratedCodeDescriptor = new DiagnosticDescriptor(
                "GeneratedCodeDiagnostic",
                "Title1",
                "GeneratedCodeDiagnostic {0}",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public static readonly DiagnosticDescriptor NonGeneratedCodeDescriptor = new DiagnosticDescriptor(
                "UserCodeDiagnostic",
                "Title2",
                "UserCodeDiagnostic {0}",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public static readonly DiagnosticDescriptor UniqueTextFileDescriptor = new DiagnosticDescriptor(
                "UniqueTextFileDiagnostic",
                "Title3",
                "UniqueTextFileDiagnostic {0}",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public static readonly DiagnosticDescriptor NumberOfUniqueTextFileDescriptor = new DiagnosticDescriptor(
                "NumberOfUniqueTextFileDescriptor",
                "Title4",
                "NumberOfUniqueTextFileDescriptor {0}",
                "Category",
                DiagnosticSeverity.Warning,
                true);

            public SharedStateAnalyzer()
            {
                _treeValueProvider = new SyntaxTreeValueProvider<bool>(IsGeneratedCode);
                _treeCallbackSet = new HashSet<SyntaxTree>(SyntaxTreeComparer.Instance);

                _textValueProvider = new SourceTextValueProvider<int>(GetCharacterCount);
                _textCallbackSet = new HashSet<SourceText>(SourceTextComparer.Instance);
            }

            private bool IsGeneratedCode(SyntaxTree tree)
            {
                lock (_treeCallbackSet)
                {
                    if (!_treeCallbackSet.Add(tree))
                    {
                        throw new Exception("Expected driver to make a single callback per tree");
                    }
                }

                var fileNameWithoutExtension = PathUtilities.GetFileName(tree.FilePath, includeExtension: false);
                return fileNameWithoutExtension.EndsWith(".designer", StringComparison.OrdinalIgnoreCase) ||
                    fileNameWithoutExtension.EndsWith(".generated", StringComparison.OrdinalIgnoreCase);
            }

            private int GetCharacterCount(SourceText text)
            {
                lock (_textCallbackSet)
                {
                    if (!_textCallbackSet.Add(text))
                    {
                        throw new Exception("Expected driver to make a single callback per text");
                    }
                }

                return text.Length;
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GeneratedCodeDescriptor, NonGeneratedCodeDescriptor, UniqueTextFileDescriptor, NumberOfUniqueTextFileDescriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(this.OnCompilationStart);
            }

            private void OnCompilationStart(CompilationStartAnalysisContext context)
            {
                context.RegisterSymbolAction(symbolContext =>
                {
                    var descriptor = GeneratedCodeDescriptor;
                    foreach (var location in symbolContext.Symbol.Locations)
                    {
                        context.TryGetValue(location.SourceTree, _treeValueProvider, out var isGeneratedCode);
                        if (!isGeneratedCode)
                        {
                            descriptor = NonGeneratedCodeDescriptor;
                            break;
                        }
                    }

                    var diagnostic = Diagnostic.Create(descriptor, symbolContext.Symbol.Locations[0], symbolContext.Symbol.Name);
                    symbolContext.ReportDiagnostic(diagnostic);
                }, SymbolKind.NamedType);

                context.RegisterSyntaxTreeAction(treeContext =>
                {
                    context.TryGetValue(treeContext.Tree, _treeValueProvider, out var isGeneratedCode);
                    var descriptor = isGeneratedCode ? GeneratedCodeDescriptor : NonGeneratedCodeDescriptor;

                    var diagnostic = Diagnostic.Create(descriptor, Location.None, treeContext.Tree.FilePath);
                    treeContext.ReportDiagnostic(diagnostic);

                    context.TryGetValue(treeContext.Tree.GetText(), _textValueProvider, out var length);
                    diagnostic = Diagnostic.Create(UniqueTextFileDescriptor, Location.None, treeContext.Tree.FilePath);
                    treeContext.ReportDiagnostic(diagnostic);
                });

                context.RegisterCompilationEndAction(endContext =>
                {
                    if (_treeCallbackSet.Count != endContext.Compilation.SyntaxTrees.Count())
                    {
                        throw new Exception("Expected driver to make a callback for every tree");
                    }

                    var diagnostic = Diagnostic.Create(NumberOfUniqueTextFileDescriptor, Location.None, _textCallbackSet.Count);
                    endContext.ReportDiagnostic(diagnostic);
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class AnalyzerForParameters : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor ParameterDescriptor = new DiagnosticDescriptor(
                "Parameter_ID",
                "Parameter_Title",
                "Parameter_Message",
                "Parameter_Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(ParameterDescriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(SymbolAction, SymbolKind.Parameter);
            }

            private void SymbolAction(SymbolAnalysisContext context)
            {
                context.ReportDiagnostic(Diagnostic.Create(ParameterDescriptor, context.Symbol.Locations[0]));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class SymbolStartAnalyzer : DiagnosticAnalyzer
        {
            private readonly SymbolKind _symbolKind;
            private readonly bool _topLevelAction;
            private readonly OperationKind? _operationKind;
            private readonly string _analyzerId;

            public SymbolStartAnalyzer(bool topLevelAction, SymbolKind symbolKind, OperationKind? operationKindOpt = null, int? analyzerId = null)
            {
                _topLevelAction = topLevelAction;
                _symbolKind = symbolKind;
                _operationKind = operationKindOpt;
                _analyzerId = $"Analyzer{(analyzerId.HasValue ? analyzerId.Value : 1)}";
                SymbolsStarted = new ConcurrentSet<ISymbol>();
            }

            internal ConcurrentSet<ISymbol> SymbolsStarted { get; }

            public static readonly DiagnosticDescriptor SymbolStartTopLevelRule = new DiagnosticDescriptor(
                "SymbolStartTopLevelRuleId",
                "SymbolStartTopLevelRuleTitle",
                "Symbol : {0}, Analyzer: {1}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor SymbolStartCompilationLevelRule = new DiagnosticDescriptor(
                "SymbolStartRuleId",
                "SymbolStartRuleTitle",
                "Symbol : {0}, Analyzer: {1}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor SymbolStartedEndedDifferRule = new DiagnosticDescriptor(
                "SymbolStartedEndedDifferRuleId",
                "SymbolStartedEndedDifferRuleTitle",
                "Symbols Started: '{0}', Symbols Ended: '{1}', Analyzer: {2}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor SymbolStartedOrderingRule = new DiagnosticDescriptor(
               "SymbolStartedOrderingRuleId",
               "SymbolStartedOrderingRuleTitle",
               "Member '{0}' started before container '{1}', Analyzer: {2}",
               "Category",
               defaultSeverity: DiagnosticSeverity.Warning,
               isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor SymbolEndedOrderingRule = new DiagnosticDescriptor(
               "SymbolEndedOrderingRuleId",
               "SymbolEndedOrderingRuleTitle",
               "Container '{0}' ended before member '{1}', Analyzer: {2}",
               "Category",
               defaultSeverity: DiagnosticSeverity.Warning,
               isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor OperationOrderingRule = new DiagnosticDescriptor(
               "OperationOrderingRuleId",
               "OperationOrderingRuleTitle",
               "Container '{0}' started after operation '{1}', Analyzer: {2}",
               "Category",
               defaultSeverity: DiagnosticSeverity.Warning,
               isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor DuplicateStartActionRule = new DiagnosticDescriptor(
               "DuplicateStartActionRuleId",
               "DuplicateStartActionRuleTitle",
               "Symbol : {0}, Analyzer: {1}",
               "Category",
               defaultSeverity: DiagnosticSeverity.Warning,
               isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor DuplicateEndActionRule = new DiagnosticDescriptor(
              "DuplicateEndActionRuleId",
              "DuplicateEndActionRuleTitle",
              "Symbol : {0}, Analyzer: {1}",
              "Category",
              defaultSeverity: DiagnosticSeverity.Warning,
              isEnabledByDefault: true);

            public static readonly DiagnosticDescriptor OperationRule = new DiagnosticDescriptor(
                "OperationRuleId",
                "OperationRuleTitle",
                "Symbol Started: '{0}', Owning Symbol: '{1}' Operation : {2}, Analyzer: {3}",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
            {
                get
                {
                    return ImmutableArray.Create(
                        SymbolStartTopLevelRule,
                        SymbolStartCompilationLevelRule,
                        SymbolStartedEndedDifferRule,
                        SymbolStartedOrderingRule,
                        SymbolEndedOrderingRule,
                        DuplicateStartActionRule,
                        DuplicateEndActionRule,
                        OperationRule,
                        OperationOrderingRule);
                }
            }

            public override void Initialize(AnalysisContext context)
            {
                var diagnostics = new ConcurrentBag<Diagnostic>();
                var symbolsEnded = new ConcurrentSet<ISymbol>();
                var seenOperationContainers = new ConcurrentDictionary<OperationAnalysisContext, ISet<ISymbol>>();

                if (_topLevelAction)
                {
                    context.RegisterSymbolStartAction(onSymbolStart, _symbolKind);

                    context.RegisterCompilationStartAction(compilationStartContext =>
                    {
                        compilationStartContext.RegisterCompilationEndAction(compilationEndContext =>
                        {
                            reportDiagnosticsAtCompilationEnd(compilationEndContext);
                        });
                    });
                }
                else
                {
                    context.RegisterCompilationStartAction(compilationStartContext =>
                    {
                        compilationStartContext.RegisterSymbolStartAction(onSymbolStart, _symbolKind);

                        compilationStartContext.RegisterCompilationEndAction(compilationEndContext =>
                        {
                            reportDiagnosticsAtCompilationEnd(compilationEndContext);
                        });
                    });
                }

                return;

                void onSymbolStart(SymbolStartAnalysisContext symbolStartContext)
                {
                    performSymbolStartActionVerification(symbolStartContext);

                    if (_operationKind.HasValue)
                    {
                        symbolStartContext.RegisterOperationAction(operationContext =>
                        {
                            performOperationActionVerification(operationContext, symbolStartContext);
                        }, _operationKind.Value);
                    }

                    symbolStartContext.RegisterSymbolEndAction(symbolEndContext =>
                    {
                        performSymbolEndActionVerification(symbolEndContext, symbolStartContext);
                    });
                }

                void reportDiagnosticsAtCompilationEnd(CompilationAnalysisContext compilationEndContext)
                {
                    if (!SymbolsStarted.SetEquals(symbolsEnded))
                    {
                        // Symbols Started: '{0}', Symbols Ended: '{1}', Analyzer: {2}
                        var symbolsStartedStr = string.Join(", ", SymbolsStarted.Select(s => s.ToDisplayString().Order()));
                        var symbolsEndedStr = string.Join(", ", symbolsEnded.Select(s => s.ToDisplayString().Order()));
                        compilationEndContext.ReportDiagnostic(Diagnostic.Create(SymbolStartedEndedDifferRule, Location.None, symbolsStartedStr, symbolsEndedStr, _analyzerId));
                    }

                    foreach (var diagnostic in diagnostics)
                    {
                        compilationEndContext.ReportDiagnostic(diagnostic);
                    }
                }

                void performSymbolStartActionVerification(SymbolStartAnalysisContext symbolStartContext)
                {
                    verifySymbolStartOrdering(symbolStartContext);
                    verifySymbolStartAndOperationOrdering(symbolStartContext);
                    if (!SymbolsStarted.Add(symbolStartContext.Symbol))
                    {
                        diagnostics.Add(Diagnostic.Create(DuplicateStartActionRule, Location.None, symbolStartContext.Symbol.Name, _analyzerId));
                    }
                }

                void performSymbolEndActionVerification(SymbolAnalysisContext symbolEndContext, SymbolStartAnalysisContext symbolStartContext)
                {
                    Assert.Equal(symbolStartContext.Symbol, symbolEndContext.Symbol);
                    verifySymbolEndOrdering(symbolEndContext);
                    if (!symbolsEnded.Add(symbolEndContext.Symbol))
                    {
                        diagnostics.Add(Diagnostic.Create(DuplicateEndActionRule, Location.None, symbolEndContext.Symbol.Name, _analyzerId));
                    }

                    Assert.False(symbolEndContext.Symbol.IsImplicitlyDeclared);
                    var rule = _topLevelAction ? SymbolStartTopLevelRule : SymbolStartCompilationLevelRule;
                    symbolEndContext.ReportDiagnostic(Diagnostic.Create(rule, Location.None, symbolStartContext.Symbol.Name, _analyzerId));
                }

                void performOperationActionVerification(OperationAnalysisContext operationContext, SymbolStartAnalysisContext symbolStartContext)
                {
                    var containingSymbols = GetContainingSymbolsAndThis(operationContext.ContainingSymbol).ToSet();
                    seenOperationContainers.Add(operationContext, containingSymbols);
                    Assert.Contains(symbolStartContext.Symbol, containingSymbols);
                    Assert.All(containingSymbols, s => Assert.DoesNotContain(s, symbolsEnded));
                    // Symbol Started: '{0}', Owning Symbol: '{1}' Operation : {2}, Analyzer: {3}
                    operationContext.ReportDiagnostic(Diagnostic.Create(OperationRule, Location.None, symbolStartContext.Symbol.Name, operationContext.ContainingSymbol.Name, operationContext.Operation.Syntax.ToString(), _analyzerId));
                }

                IEnumerable<ISymbol> GetContainingSymbolsAndThis(ISymbol symbol)
                {
                    do
                    {
                        yield return symbol;
                        symbol = symbol.ContainingSymbol;
                    }
                    while (symbol != null && !symbol.IsImplicitlyDeclared);
                }

                void verifySymbolStartOrdering(SymbolStartAnalysisContext symbolStartContext)
                {
                    ISymbol symbolStarted = symbolStartContext.Symbol;
                    IEnumerable<ISymbol> members;
                    switch (symbolStarted)
                    {
                        case INamedTypeSymbol namedType:
                            members = namedType.GetMembers();
                            break;

                        case INamespaceSymbol namespaceSym:
                            members = namespaceSym.GetMembers();
                            break;

                        default:
                            return;
                    }

                    foreach (var member in members.Where(m => !m.IsImplicitlyDeclared))
                    {
                        if (SymbolsStarted.Contains(member))
                        {
                            // Member '{0}' started before container '{1}', Analyzer {2}
                            diagnostics.Add(Diagnostic.Create(SymbolStartedOrderingRule, Location.None, member, symbolStarted, _analyzerId));
                        }
                    }
                }

                void verifySymbolEndOrdering(SymbolAnalysisContext symbolEndContext)
                {
                    ISymbol symbolEnded = symbolEndContext.Symbol;
                    IList<ISymbol> containersToVerify = new List<ISymbol>();
                    if (symbolEnded.ContainingType != null)
                    {
                        containersToVerify.Add(symbolEnded.ContainingType);
                    }

                    if (symbolEnded.ContainingNamespace != null)
                    {
                        containersToVerify.Add(symbolEnded.ContainingNamespace);
                    }

                    foreach (var container in containersToVerify)
                    {
                        if (symbolsEnded.Contains(container))
                        {
                            // Container '{0}' ended before member '{1}', Analyzer {2}
                            diagnostics.Add(Diagnostic.Create(SymbolEndedOrderingRule, Location.None, container, symbolEnded, _analyzerId));
                        }
                    }
                }

                void verifySymbolStartAndOperationOrdering(SymbolStartAnalysisContext symbolStartContext)
                {
                    foreach (var kvp in seenOperationContainers)
                    {
                        OperationAnalysisContext operationContext = kvp.Key;
                        ISet<ISymbol> containers = kvp.Value;

                        if (containers.Contains(symbolStartContext.Symbol))
                        {
                            // Container '{0}' started after operation '{1}', Analyzer {2}
                            diagnostics.Add(Diagnostic.Create(OperationOrderingRule, Location.None, symbolStartContext.Symbol, operationContext.Operation.Syntax.ToString(), _analyzerId));
                        }
                    }
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressorForId : DiagnosticSuppressor
        {
            public SuppressionDescriptor SuppressionDescriptor { get; }
            public DiagnosticSuppressorForId(string suppressedDiagnosticId, string suppressionId = null)
            {
                SuppressionDescriptor = new SuppressionDescriptor(
                    id: suppressionId ?? "SPR0001",
                    suppressedDiagnosticId: suppressedDiagnosticId,
                    justification: $"Suppress {suppressedDiagnosticId}");
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(SuppressionDescriptor);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                foreach (var diagnostic in context.ReportedDiagnostics)
                {
                    Assert.Equal(SuppressionDescriptor.SuppressedDiagnosticId, diagnostic.Id);
                    context.ReportSuppression(Suppression.Create(SuppressionDescriptor, diagnostic));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressorForMultipleIds : DiagnosticSuppressor
        {
            public DiagnosticSuppressorForMultipleIds(params string[] suppressedDiagnosticIds)
            {
                var builder = ImmutableArray.CreateBuilder<SuppressionDescriptor>();
                int i = 1;
                foreach (var suppressedDiagnosticId in suppressedDiagnosticIds)
                {
                    var descriptor = new SuppressionDescriptor(
                        id: $"SPR000{i++}",
                        suppressedDiagnosticId: suppressedDiagnosticId,
                        justification: $"Suppress {suppressedDiagnosticId}");
                    builder.Add(descriptor);
                }

                SupportedSuppressions = builder.ToImmutable();
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; }

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                foreach (var diagnostic in context.ReportedDiagnostics)
                {
                    foreach (var suppressionDescriptor in SupportedSuppressions)
                    {
                        if (suppressionDescriptor.SuppressedDiagnosticId == diagnostic.Id)
                        {
                            context.ReportSuppression(Suppression.Create(suppressionDescriptor, diagnostic));
                        }
                    }
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressorForId_ThrowsOperationCancelledException : DiagnosticSuppressor
        {
            public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
            public SuppressionDescriptor SuppressionDescriptor { get; }
            public DiagnosticSuppressorForId_ThrowsOperationCancelledException(string suppressedDiagnosticId)
            {
                SuppressionDescriptor = new SuppressionDescriptor(
                    id: "SPR0001",
                    suppressedDiagnosticId: suppressedDiagnosticId,
                    justification: $"Suppress {suppressedDiagnosticId}");
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(SuppressionDescriptor);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                CancellationTokenSource.Cancel();
                context.CancellationToken.ThrowIfCancellationRequested();
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressorThrowsExceptionFromSupportedSuppressions : DiagnosticSuppressor
        {
            private readonly NotImplementedException _exception;

            public DiagnosticSuppressorThrowsExceptionFromSupportedSuppressions(NotImplementedException exception)
            {
                _exception = exception;
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => throw _exception;

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressorThrowsExceptionFromReportedSuppressions : DiagnosticSuppressor
        {
            private readonly SuppressionDescriptor _descriptor;
            private readonly NotImplementedException _exception;

            public DiagnosticSuppressorThrowsExceptionFromReportedSuppressions(string suppressedDiagnosticId, NotImplementedException exception)
            {
                _descriptor = new SuppressionDescriptor(
                    "SPR0001",
                    suppressedDiagnosticId,
                    $"Suppress {suppressedDiagnosticId}");
                _exception = exception;
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(_descriptor);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                throw _exception;
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressor_UnsupportedSuppressionReported : DiagnosticSuppressor
        {
            private readonly SuppressionDescriptor _supportedDescriptor;
            private readonly SuppressionDescriptor _unsupportedDescriptor;

            public DiagnosticSuppressor_UnsupportedSuppressionReported(string suppressedDiagnosticId, string supportedSuppressionId, string unsupportedSuppressionId)
            {
                _supportedDescriptor = new SuppressionDescriptor(
                    supportedSuppressionId,
                    suppressedDiagnosticId,
                    $"Suppress {suppressedDiagnosticId}");

                _unsupportedDescriptor = new SuppressionDescriptor(
                    unsupportedSuppressionId,
                    suppressedDiagnosticId,
                    $"Suppress {suppressedDiagnosticId}");
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(_supportedDescriptor);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                foreach (var diagnostic in context.ReportedDiagnostics)
                {
                    Assert.Equal(_unsupportedDescriptor.SuppressedDiagnosticId, diagnostic.Id);
                    context.ReportSuppression(Suppression.Create(_unsupportedDescriptor, diagnostic));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressor_InvalidDiagnosticSuppressionReported : DiagnosticSuppressor
        {
            private readonly SuppressionDescriptor _supportedDescriptor;
            private readonly SuppressionDescriptor _unsupportedDescriptor;

            public DiagnosticSuppressor_InvalidDiagnosticSuppressionReported(string suppressedDiagnosticId, string unsupportedSuppressedDiagnosticId)
            {
                _supportedDescriptor = new SuppressionDescriptor(
                    "SPR0001",
                    suppressedDiagnosticId,
                    $"Suppress {suppressedDiagnosticId}");

                _unsupportedDescriptor = new SuppressionDescriptor(
                    "SPR0002",
                    unsupportedSuppressedDiagnosticId,
                    $"Suppress {unsupportedSuppressedDiagnosticId}");
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(_supportedDescriptor);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                foreach (var diagnostic in context.ReportedDiagnostics)
                {
                    Assert.Equal(_supportedDescriptor.SuppressedDiagnosticId, diagnostic.Id);
                    context.ReportSuppression(Suppression.Create(_unsupportedDescriptor, diagnostic));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class DiagnosticSuppressor_NonReportedDiagnosticCannotBeSuppressed : DiagnosticSuppressor
        {
            private readonly SuppressionDescriptor _descriptor1, _descriptor2;
            private readonly string _nonReportedDiagnosticId;

            public DiagnosticSuppressor_NonReportedDiagnosticCannotBeSuppressed(string reportedDiagnosticId, string nonReportedDiagnosticId)
            {
                _descriptor1 = new SuppressionDescriptor(
                    "SPR0001",
                    reportedDiagnosticId,
                    $"Suppress {reportedDiagnosticId}");
                _descriptor2 = new SuppressionDescriptor(
                    "SPR0002",
                    nonReportedDiagnosticId,
                    $"Suppress {nonReportedDiagnosticId}");
                _nonReportedDiagnosticId = nonReportedDiagnosticId;
            }

            public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
                => ImmutableArray.Create(_descriptor1, _descriptor2);

            public override void ReportSuppressions(SuppressionAnalysisContext context)
            {
                var nonReportedDiagnostic = Diagnostic.Create(
                    id: _nonReportedDiagnosticId,
                    category: "Category",
                    message: "Message",
                    severity: DiagnosticSeverity.Warning,
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true,
                    warningLevel: 1);
                context.ReportSuppression(Suppression.Create(_descriptor2, nonReportedDiagnostic));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class NamedTypeAnalyzer : DiagnosticAnalyzer
        {
            public enum AnalysisKind
            {
                Symbol,
                SymbolStartEnd,
                CompilationStartEnd
            }

            public const string RuleId = "ID1";
            public const string RuleCategory = "Category1";
            private readonly DiagnosticDescriptor _rule;
            private readonly AnalysisKind _analysisKind;
            private readonly GeneratedCodeAnalysisFlags _analysisFlags;
            private readonly ConcurrentSet<ISymbol> _symbolCallbacks;

            public NamedTypeAnalyzer(AnalysisKind analysisKind, GeneratedCodeAnalysisFlags analysisFlags = GeneratedCodeAnalysisFlags.None, bool configurable = true)
            {
                _analysisKind = analysisKind;
                _analysisFlags = analysisFlags;
                _symbolCallbacks = new ConcurrentSet<ISymbol>();

                var customTags = configurable ? Array.Empty<string>() : new[] { WellKnownDiagnosticTags.NotConfigurable };
                _rule = new DiagnosticDescriptor(
                    RuleId,
                    "Title1",
                    "Symbol: {0}",
                    RuleCategory,
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true,
                    customTags: customTags);
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_rule);
            public string GetSortedSymbolCallbacksString() => string.Join(", ", _symbolCallbacks.Select(s => s.Name).Order());

            public override void Initialize(AnalysisContext context)
            {
                context.ConfigureGeneratedCodeAnalysis(_analysisFlags);

                switch (_analysisKind)
                {
                    case AnalysisKind.Symbol:
                        context.RegisterSymbolAction(c =>
                            {
                                _symbolCallbacks.Add(c.Symbol);
                                ReportDiagnostic(c.Symbol, c.ReportDiagnostic);
                            }, SymbolKind.NamedType);
                        break;

                    case AnalysisKind.SymbolStartEnd:
                        context.RegisterSymbolStartAction(symbolStartContext =>
                        {
                            symbolStartContext.RegisterSymbolEndAction(symbolEndContext =>
                            {
                                _symbolCallbacks.Add(symbolEndContext.Symbol);
                                ReportDiagnostic(symbolEndContext.Symbol, symbolEndContext.ReportDiagnostic);
                            });
                        }, SymbolKind.NamedType);

                        break;

                    case AnalysisKind.CompilationStartEnd:
                        context.RegisterCompilationStartAction(compilationStartContext =>
                        {
                            compilationStartContext.RegisterSymbolAction(c =>
                            {
                                _symbolCallbacks.Add(c.Symbol);
                            }, SymbolKind.NamedType);

                            compilationStartContext.RegisterCompilationEndAction(
                                compilationEndContext => compilationEndContext.ReportDiagnostic(
                                    Diagnostic.Create(_rule, Location.None, GetSortedSymbolCallbacksString())));
                        });

                        break;
                }
            }

            private void ReportDiagnostic(ISymbol symbol, Action<Diagnostic> reportDiagnostic)
                => reportDiagnostic(Diagnostic.Create(_rule, symbol.Locations[0], symbol.Name));
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class AnalyzerWithNoLocationDiagnostics : DiagnosticAnalyzer
        {
            public AnalyzerWithNoLocationDiagnostics(bool isEnabledByDefault)
            {
                Descriptor = new DiagnosticDescriptor(
                    "ID0001",
                    "Title1",
                    "Message1",
                    "Category1",
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationAction(compilationContext =>
                    compilationContext.ReportDiagnostic(Diagnostic.Create(Descriptor, Location.None)));
            }
        }

        /// <summary>
        /// An analyzer that reports a diagnostic on every single named type.
        /// </summary>
        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class NamedTypeAnalyzerWithConfigurableEnabledByDefault : DiagnosticAnalyzer
        {
            private readonly bool _throwOnAllNamedTypes;
            private readonly DiagnosticSeverity _reportedSeverity;

            public NamedTypeAnalyzerWithConfigurableEnabledByDefault(bool isEnabledByDefault, DiagnosticSeverity defaultSeverity, bool throwOnAllNamedTypes = false)
                : this(isEnabledByDefault, defaultSeverity, customConfigurable: false, throwOnAllNamedTypes)
            {
            }

            public NamedTypeAnalyzerWithConfigurableEnabledByDefault(bool isEnabledByDefault, DiagnosticSeverity defaultSeverity, bool customConfigurable, bool throwOnAllNamedTypes)
                : this(isEnabledByDefault, defaultSeverity, defaultSeverity, customConfigurable, throwOnAllNamedTypes)
            {
            }

            public NamedTypeAnalyzerWithConfigurableEnabledByDefault(
                bool isEnabledByDefault,
                DiagnosticSeverity defaultSeverity,
                DiagnosticSeverity reportedSeverity,
                bool customConfigurable,
                bool throwOnAllNamedTypes)
            {
                var customTags = Array.Empty<string>();
                if (customConfigurable)
                    customTags = [WellKnownDiagnosticTags.CustomSeverityConfigurable];

                Descriptor = new DiagnosticDescriptor(
                    "ID0001",
                    "Title1",
                    "Message1",
                    "Category1",
                    defaultSeverity,
                    isEnabledByDefault,
                    customTags: customTags);

                _throwOnAllNamedTypes = throwOnAllNamedTypes;
                _reportedSeverity = reportedSeverity;
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSymbolAction(context =>
                {
                    if (_throwOnAllNamedTypes)
                    {
                        throw new NotImplementedException();
                    }

                    var diagnostic = Diagnostic.Create(Descriptor, context.Symbol.Locations[0], _reportedSeverity, additionalLocations: null, properties: null, messageArgs: null);
                    context.ReportDiagnostic(diagnostic);
                },
                SymbolKind.NamedType);
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class RegisterOperationBlockAndOperationActionAnalyzer : DiagnosticAnalyzer
        {
            private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor(
                "ID0001",
                "Title",
                "Message",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
            public override void Initialize(AnalysisContext analysisContext)
            {
                analysisContext.RegisterOperationAction(_ => { }, OperationKind.Invocation);
                analysisContext.RegisterOperationBlockStartAction(OnOperationBlockStart);
            }

            private void OnOperationBlockStart(OperationBlockStartAnalysisContext context)
            {
                context.RegisterOperationBlockEndAction(
                    endContext => endContext.ReportDiagnostic(Diagnostic.Create(s_descriptor, context.OwningSymbol.Locations[0])));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public sealed class FieldAnalyzer : DiagnosticAnalyzer
        {
            private readonly bool _syntaxTreeAction;
            public FieldAnalyzer(string diagnosticId, bool syntaxTreeAction)
            {
                _syntaxTreeAction = syntaxTreeAction;
                Descriptor = new DiagnosticDescriptor(
                    diagnosticId,
                    "Title",
                    "Message",
                    "Category",
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);

            public override void Initialize(AnalysisContext context)
            {
                if (_syntaxTreeAction)
                {
                    context.RegisterSyntaxTreeAction(context =>
                    {
                        var fields = context.Tree.GetRoot().DescendantNodes().OfType<CSharp.Syntax.FieldDeclarationSyntax>();
                        foreach (var variable in fields.SelectMany(f => f.Declaration.Variables))
                        {
                            context.ReportDiagnostic(Diagnostic.Create(Descriptor, variable.Identifier.GetLocation()));
                        }
                    });
                }
                else
                {
                    context.RegisterSymbolAction(
                        context => context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Symbol.Locations[0])),
                        SymbolKind.Field);
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class AdditionalFileAnalyzer : DiagnosticAnalyzer
        {
            private readonly bool _registerFromInitialize;
            private readonly TextSpan _diagnosticSpan;

            public AdditionalFileAnalyzer(bool registerFromInitialize, TextSpan diagnosticSpan, string id = "ID0001")
            {
                _registerFromInitialize = registerFromInitialize;
                _diagnosticSpan = diagnosticSpan;

                Descriptor = new DiagnosticDescriptor(
                    id,
                    "Title1",
                    "Message1",
                    "Category1",
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
            public override void Initialize(AnalysisContext context)
            {
                if (_registerFromInitialize)
                {
                    context.RegisterAdditionalFileAction(AnalyzeAdditionalFile);
                }
                else
                {
                    context.RegisterCompilationStartAction(context =>
                        context.RegisterAdditionalFileAction(AnalyzeAdditionalFile));
                }
            }

            private void AnalyzeAdditionalFile(AdditionalFileAnalysisContext context)
            {
                if (context.AdditionalFile.Path == null)
                {
                    return;
                }

                var text = context.AdditionalFile.GetText();
                var location = Location.Create(context.AdditionalFile.Path, _diagnosticSpan, text.Lines.GetLinePositionSpan(_diagnosticSpan));
                context.ReportDiagnostic(Diagnostic.Create(Descriptor, location));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public sealed class RegisterSyntaxTreeCancellationAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = "ID0001";
            private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor(
                DiagnosticId,
                "Title",
                "Message",
                "Category",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);
            private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
            public CancellationToken CancellationToken => _cancellationTokenSource.Token;

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
            public override void Initialize(AnalysisContext context)
            {
                context.RegisterSyntaxTreeAction(context =>
                {
                    // Mimic cancellation by throwing an OperationCanceledException in first callback.
                    if (!_cancellationTokenSource.IsCancellationRequested)
                    {
                        _cancellationTokenSource.Cancel();

                        while (true)
                        {
                            context.CancellationToken.ThrowIfCancellationRequested();
                        }

                        throw ExceptionUtilities.Unreachable();
                    }

                    context.ReportDiagnostic(Diagnostic.Create(s_descriptor, context.Tree.GetRoot().GetLocation()));
                });
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public sealed class LocalNonLocalDiagnosticsAnalyzer : DiagnosticAnalyzer
        {
            private static readonly DiagnosticDescriptor s_descriptor = new("ID0001", "Title", "{0}", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);
            private readonly ActionKind _actionKind;

            public enum ActionKind
            {
                SyntaxTreeAction,
                SemanticModelAction,
                SymbolAction,
                SymbolStartEndAction,
                OperationAction,
                OperationBlockAction,
                OperationBlockStartEndAction,
                SyntaxNodeAction,
                CodeBlockAction,
                CodeBlockStartEndAction
            }

            public LocalNonLocalDiagnosticsAnalyzer(ActionKind actionKind)
            {
                _actionKind = actionKind;
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(AnalyzeCompilation);
            }

            private void AnalyzeCompilation(CompilationStartAnalysisContext context)
            {
                switch (_actionKind)
                {
                    case ActionKind.SyntaxTreeAction:
                        context.RegisterSyntaxTreeAction(treeContext =>
                            ReportDiagnosticsInAllTrees("RegisterSyntaxTreeAction", treeContext.Tree, treeContext.Compilation, treeContext.ReportDiagnostic));
                        break;

                    case ActionKind.SemanticModelAction:
                        context.RegisterSemanticModelAction(semanticModelContext =>
                            ReportDiagnosticsInAllTrees("RegisterSemanticModelAction", semanticModelContext.SemanticModel.SyntaxTree, semanticModelContext.SemanticModel.Compilation, semanticModelContext.ReportDiagnostic));
                        break;

                    case ActionKind.SymbolAction:
                        context.RegisterSymbolAction(symbolContext =>
                            ReportSymbolDiagnostics("RegisterSymbolAction", symbolContext.Symbol, symbolContext.Compilation, symbolContext.ReportDiagnostic),
                            SymbolKind.NamedType, SymbolKind.Method);
                        break;

                    case ActionKind.SymbolStartEndAction:
                        context.RegisterSymbolStartAction(symbolStartContext =>
                        {
                            symbolStartContext.RegisterOperationAction(operationContext =>
                                ReportDiagnostics($"RegisterOperationAction({operationContext.Operation.Syntax}) in RegisterSymbolStartAction", operationContext.Operation.Syntax.SyntaxTree, operationContext.ContainingSymbol, operationContext.ReportDiagnostic),
                                OperationKind.VariableDeclarationGroup);

                            symbolStartContext.RegisterSyntaxNodeAction(syntaxNodeContext =>
                                ReportDiagnostics($"RegisterSyntaxNodeAction({syntaxNodeContext.Node}) in RegisterSymbolStartAction", syntaxNodeContext.Node.SyntaxTree, syntaxNodeContext.ContainingSymbol, syntaxNodeContext.ReportDiagnostic),
                                SyntaxKind.LocalDeclarationStatement);

                            symbolStartContext.RegisterSymbolEndAction(symbolEndContext =>
                                ReportSymbolDiagnostics("RegisterSymbolEndAction", symbolEndContext.Symbol, symbolEndContext.Compilation, symbolEndContext.ReportDiagnostic));
                        }, SymbolKind.NamedType);
                        break;

                    case ActionKind.OperationAction:
                        context.RegisterOperationAction(operationContext =>
                            ReportDiagnostics($"RegisterOperationAction({operationContext.Operation.Syntax})", operationContext.Operation.Syntax.SyntaxTree, operationContext.ContainingSymbol, operationContext.ReportDiagnostic),
                            OperationKind.VariableDeclarationGroup);
                        break;

                    case ActionKind.OperationBlockAction:
                        context.RegisterOperationBlockAction(operationBlockContext =>
                            ReportDiagnostics("RegisterOperationBlockAction", operationBlockContext.OwningSymbol.DeclaringSyntaxReferences[0].SyntaxTree, operationBlockContext.OwningSymbol, operationBlockContext.ReportDiagnostic));
                        break;

                    case ActionKind.OperationBlockStartEndAction:
                        context.RegisterOperationBlockStartAction(operationBlockStartContext =>
                        {
                            operationBlockStartContext.RegisterOperationAction(operationContext =>
                                ReportDiagnostics($"RegisterOperationAction({operationContext.Operation.Syntax}) in RegisterOperationBlockStartAction", operationContext.Operation.Syntax.SyntaxTree, operationContext.ContainingSymbol, operationContext.ReportDiagnostic),
                                OperationKind.VariableDeclarationGroup);

                            operationBlockStartContext.RegisterOperationBlockEndAction(operationBlockEndContext =>
                                ReportDiagnostics("RegisterOperationBlockEndAction", operationBlockEndContext.OwningSymbol.DeclaringSyntaxReferences[0].SyntaxTree, operationBlockEndContext.OwningSymbol, operationBlockEndContext.ReportDiagnostic));
                        });
                        break;

                    case ActionKind.SyntaxNodeAction:
                        context.RegisterSyntaxNodeAction(syntaxNodeContext =>
                            ReportDiagnostics($"RegisterSyntaxNodeAction({syntaxNodeContext.Node})", syntaxNodeContext.Node.SyntaxTree, syntaxNodeContext.ContainingSymbol, syntaxNodeContext.ReportDiagnostic),
                            SyntaxKind.LocalDeclarationStatement);
                        break;

                    case ActionKind.CodeBlockAction:
                        context.RegisterCodeBlockAction(codeBlockContext =>
                            ReportDiagnostics("RegisterCodeBlockAction", codeBlockContext.CodeBlock.SyntaxTree, codeBlockContext.OwningSymbol, codeBlockContext.ReportDiagnostic));
                        break;

                    case ActionKind.CodeBlockStartEndAction:
                        context.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockStartContext =>
                        {
                            codeBlockStartContext.RegisterSyntaxNodeAction(syntaxNodeContext =>
                                ReportDiagnostics($"RegisterSyntaxNodeAction({syntaxNodeContext.Node}) in RegisterCodeBlockStartAction", syntaxNodeContext.Node.SyntaxTree, syntaxNodeContext.ContainingSymbol, syntaxNodeContext.ReportDiagnostic),
                                SyntaxKind.LocalDeclarationStatement);

                            codeBlockStartContext.RegisterCodeBlockEndAction(codeBlockEndContext =>
                                ReportDiagnostics("RegisterCodeBlockEndAction", codeBlockEndContext.CodeBlock.SyntaxTree, codeBlockEndContext.OwningSymbol, codeBlockEndContext.ReportDiagnostic));
                        });
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(_actionKind);
                }
            }

            private static void ReportSymbolDiagnostics(string actionName, ISymbol symbol, Compilation compilation, Action<Diagnostic> reportDiagnostic)
            {
                var arg = $"{actionName}({symbol.Name})";
                var trees = symbol.DeclaringSyntaxReferences.Select(syntaxRef => syntaxRef.SyntaxTree).Distinct();
                foreach (var tree in trees)
                {
                    var index = compilation.SyntaxTrees.IndexOf(tree) + 1;
                    ReportDiagnosticsInTreeCore($"{arg}(File{index})", tree, reportDiagnostic);
                }
            }

            private static void ReportDiagnosticsInAllTrees(string actionName, SyntaxTree analyzedTree, Compilation compilation, Action<Diagnostic> reportDiagnostic)
            {
                var index = compilation.SyntaxTrees.IndexOf(analyzedTree) + 1;
                foreach (var tree in compilation.SyntaxTrees)
                {
                    ReportDiagnosticsInTreeCore($"{actionName}(File{index})", tree, reportDiagnostic);
                }
            }

            private static void ReportDiagnostics(string actionName, SyntaxTree tree, ISymbol containingSymbol, Action<Diagnostic> reportDiagnostic)
            {
                var arg = $"{actionName}({containingSymbol.Name})";
                ReportDiagnosticsInTreeCore(arg, tree, reportDiagnostic);
            }

            private static void ReportDiagnosticsInTreeCore(string arg, SyntaxTree tree, Action<Diagnostic> reportDiagnostic)
            {
                var root = tree.GetRoot();
                foreach (var localDecl in root.DescendantNodes().OfType<CSharp.Syntax.LocalDeclarationStatementSyntax>())
                {
                    reportDiagnostic(Diagnostic.Create(s_descriptor, localDecl.GetLocation(), arg));
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public sealed class VariableDeclarationAnalyzer : DiagnosticAnalyzer
        {
            private readonly bool _testSyntaxNodeAction;
            public VariableDeclarationAnalyzer(string diagnosticId, bool testSyntaxNodeAction)
            {
                _testSyntaxNodeAction = testSyntaxNodeAction;
                Descriptor = new DiagnosticDescriptor(
                    diagnosticId,
                    "Title",
                    "Message",
                    "Category",
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true);
            }

            public DiagnosticDescriptor Descriptor { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);

            public override void Initialize(AnalysisContext context)
            {
                if (_testSyntaxNodeAction)
                {
                    context.RegisterSyntaxNodeAction<SyntaxKind>(
                        context => context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation())),
                        SyntaxKind.VariableDeclaration);
                }
                else
                {
                    context.RegisterOperationAction(
                        context => context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Operation.Syntax.GetLocation())),
                        OperationKind.VariableDeclaration);
                }
            }
        }

        internal enum AnalyzerRegisterActionKind
        {
            SyntaxTree,
            SyntaxNode,
            Symbol,
            Operation,
            SemanticModel,
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        internal sealed class CancellationTestAnalyzer : DiagnosticAnalyzer
        {
            public const string DiagnosticId = "DiagnosticId";
            private readonly DiagnosticDescriptor s_descriptor =
                new DiagnosticDescriptor(DiagnosticId, "test", "test", "test", DiagnosticSeverity.Warning, isEnabledByDefault: true);

            private readonly AnalyzerRegisterActionKind _actionKind;
            private readonly CancellationTokenSource _cancellationTokenSource = new();

            public CancellationTestAnalyzer(AnalyzerRegisterActionKind actionKind)
            {
                _actionKind = actionKind;
                CanceledCompilations = new ConcurrentSet<Compilation>();
            }

            public CancellationToken CancellationToken => _cancellationTokenSource.Token;
            public ConcurrentSet<Compilation> CanceledCompilations { get; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(OnCompilationStart);
            }

            private void OnCompilationStart(CompilationStartAnalysisContext context)
            {
                switch (_actionKind)
                {
                    case AnalyzerRegisterActionKind.SyntaxTree:
                        context.RegisterSyntaxTreeAction(syntaxContext => HandleCallback(syntaxContext.Tree.GetRoot().GetLocation(), context.Compilation, syntaxContext.ReportDiagnostic, syntaxContext.CancellationToken));
                        break;
                    case AnalyzerRegisterActionKind.SyntaxNode:
                        context.RegisterSyntaxNodeAction(context => HandleCallback(context.Node.GetLocation(), context.Compilation, context.ReportDiagnostic, context.CancellationToken), CodeAnalysis.CSharp.SyntaxKind.ClassDeclaration);
                        break;
                    case AnalyzerRegisterActionKind.Symbol:
                        context.RegisterSymbolAction(context => HandleCallback(context.Symbol.Locations[0], context.Compilation, context.ReportDiagnostic, context.CancellationToken), SymbolKind.NamedType);
                        break;
                    case AnalyzerRegisterActionKind.Operation:
                        context.RegisterOperationAction(context => HandleCallback(context.Operation.Syntax.GetLocation(), context.Compilation, context.ReportDiagnostic, context.CancellationToken), OperationKind.VariableDeclaration);
                        break;
                    case AnalyzerRegisterActionKind.SemanticModel:
                        context.RegisterSemanticModelAction(context => HandleCallback(context.SemanticModel.SyntaxTree.GetRoot().GetLocation(), context.SemanticModel.Compilation, context.ReportDiagnostic, context.CancellationToken));
                        break;
                }
            }

            private void HandleCallback(Location analysisLocation, Compilation compilation, Action<Diagnostic> reportDiagnostic, CancellationToken cancellationToken)
            {
                // Mimic cancellation by throwing an OperationCanceledException in first callback.
                if (!_cancellationTokenSource.IsCancellationRequested)
                {
                    _cancellationTokenSource.Cancel();
                    CanceledCompilations.Add(compilation);

                    while (true)
                    {
                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    throw ExceptionUtilities.Unreachable();
                }

                // Report diagnostic in the second callback.
                reportDiagnostic(Diagnostic.Create(s_descriptor, analysisLocation));
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public sealed class AllActionsAnalyzer : DiagnosticAnalyzer
        {
            private static readonly DiagnosticDescriptor s_descriptor = new("ID0001", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true);

            private readonly bool _testSyntaxTreeAction;
            private readonly bool _testSemanticModelAction;
            private readonly bool _testSymbolStartAction;
            private readonly bool _testBlockActions;

            public readonly List<SyntaxTree> AnalyzedTrees = new();
            public readonly List<SemanticModel> AnalyzedSemanticModels = new();
            public readonly List<ISymbol> AnalyzedSymbols = new();
            public readonly List<ISymbol> AnalyzedSymbolStartSymbols = new();
            public readonly List<ISymbol> AnalyzedSymbolEndSymbols = new();
            public readonly List<IOperation> AnalyzedOperations = new();
            public readonly List<ISymbol> AnalyzedOperationBlockSymbols = new();
            public readonly List<IOperation> AnalyzedOperationsInsideOperationBlock = new();
            public readonly List<ISymbol> AnalyzedOperationBlockStartSymbols = new();
            public readonly List<ISymbol> AnalyzedOperationBlockEndSymbols = new();
            public readonly List<SyntaxNode> AnalyzedSyntaxNodes = new();
            public readonly List<ISymbol> AnalyzedCodeBlockSymbols = new();
            public readonly List<SyntaxNode> AnalyzedSyntaxNodesInsideCodeBlock = new();
            public readonly List<ISymbol> AnalyzedCodeBlockStartSymbols = new();
            public readonly List<ISymbol> AnalyzedCodeBlockEndSymbols = new();

            public AllActionsAnalyzer(bool testSyntaxTreeAction, bool testSemanticModelAction, bool testSymbolStartAction, bool testBlockActions)
            {
                _testSyntaxTreeAction = testSyntaxTreeAction;
                _testSemanticModelAction = testSemanticModelAction;
                _testSymbolStartAction = testSymbolStartAction;
                _testBlockActions = testBlockActions;
            }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);

            public override void Initialize(AnalysisContext context)
            {
                context.RegisterCompilationStartAction(AnalyzeCompilation);
            }

            private void AnalyzeCompilation(CompilationStartAnalysisContext context)
            {
                // Unconditionally test symbol/operation/node actions.
                context.RegisterSymbolAction(symbolContext => AnalyzedSymbols.Add(symbolContext.Symbol), SymbolKind.NamedType, SymbolKind.Method);
                context.RegisterOperationAction(operationContext => AnalyzedOperations.Add(operationContext.Operation), OperationKind.VariableDeclaration);
                context.RegisterSyntaxNodeAction(syntaxNodeContext => AnalyzedSyntaxNodes.Add(syntaxNodeContext.Node), SyntaxKind.LocalDeclarationStatement);

                if (_testSyntaxTreeAction)
                {
                    context.RegisterSyntaxTreeAction(treeContext => AnalyzedTrees.Add(treeContext.Tree));
                }

                if (_testSemanticModelAction)
                {
                    context.RegisterSemanticModelAction(semanticModelContext => AnalyzedSemanticModels.Add(semanticModelContext.SemanticModel));
                }

                if (_testSymbolStartAction)
                {
                    context.RegisterSymbolStartAction(symbolStartContext =>
                    {
                        AnalyzedSymbolStartSymbols.Add(symbolStartContext.Symbol);
                        symbolStartContext.RegisterSymbolEndAction(symbolEndContext => AnalyzedSymbolEndSymbols.Add(symbolEndContext.Symbol));
                    }, SymbolKind.NamedType);
                }

                if (_testBlockActions)
                {
                    context.RegisterOperationBlockAction(operationBlockContext => AnalyzedOperationBlockSymbols.Add(operationBlockContext.OwningSymbol));
                    context.RegisterOperationBlockStartAction(operationBlockStartContext =>
                    {
                        AnalyzedOperationBlockStartSymbols.Add(operationBlockStartContext.OwningSymbol);
                        operationBlockStartContext.RegisterOperationAction(operationContext => AnalyzedOperationsInsideOperationBlock.Add(operationContext.Operation), OperationKind.VariableDeclaration);
                        operationBlockStartContext.RegisterOperationBlockEndAction(operationBlockEndContext => AnalyzedOperationBlockEndSymbols.Add(operationBlockEndContext.OwningSymbol));
                    });
                    context.RegisterCodeBlockAction(codeBlockContext => AnalyzedCodeBlockSymbols.Add(codeBlockContext.OwningSymbol));
                    context.RegisterCodeBlockStartAction<SyntaxKind>(codeBlockStartContext =>
                    {
                        AnalyzedCodeBlockStartSymbols.Add(codeBlockStartContext.OwningSymbol);
                        codeBlockStartContext.RegisterSyntaxNodeAction(syntaxNodeContext => AnalyzedSyntaxNodesInsideCodeBlock.Add(syntaxNodeContext.Node), SyntaxKind.LocalDeclarationStatement);
                        codeBlockStartContext.RegisterCodeBlockEndAction(codeBlockEndContext => AnalyzedCodeBlockEndSymbols.Add(codeBlockEndContext.OwningSymbol));
                    });
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp)]
        public sealed class FilterSpanTestAnalyzer : DiagnosticAnalyzer
        {
            public enum AnalysisKind
            {
                SyntaxTree,
                AdditionalFile,
                SemanticModel,
                Symbol,
                SymbolStart,
                Operation,
                OperationBlockStart,
                OperationBlock,
                SyntaxNode,
                CodeBlockStart,
                CodeBlock,
            }

            private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor(
                    "ID0001",
                    "Title",
                    "Message",
                    "Category",
                    defaultSeverity: DiagnosticSeverity.Warning,
                    isEnabledByDefault: true);

            private readonly AnalysisKind _analysisKind;

            public FilterSpanTestAnalyzer(AnalysisKind analysisKind)
            {
                _analysisKind = analysisKind;
            }

            public TextSpan? CallbackFilterSpan { get; private set; }
            public SyntaxTree CallbackFilterTree { get; private set; }
            public AdditionalText CallbackFilterFile { get; private set; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);

            public override void Initialize(AnalysisContext context)
            {
                switch (_analysisKind)
                {
                    case AnalysisKind.SyntaxTree:
                        context.RegisterSyntaxTreeAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.Tree;
                        });
                        break;

                    case AnalysisKind.AdditionalFile:
                        context.RegisterAdditionalFileAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterFile = context.AdditionalFile;
                        });
                        break;

                    case AnalysisKind.SemanticModel:
                        context.RegisterSemanticModelAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        });
                        break;

                    case AnalysisKind.Symbol:
                        context.RegisterSymbolAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        }, SymbolKind.NamedType);
                        break;

                    case AnalysisKind.SymbolStart:
                        context.RegisterSymbolStartAction(startContext =>
                        {
                            CallbackFilterSpan = startContext.FilterSpan;
                            CallbackFilterTree = startContext.FilterTree;

                            startContext.RegisterSymbolEndAction(endContext =>
                            {
                                if (startContext.FilterTree != endContext.FilterTree)
                                    throw new InvalidOperationException("Mismatched FilterTree");

                                if (startContext.FilterSpan != endContext.FilterSpan)
                                    throw new InvalidOperationException("Mismatched FilterSpan");
                            });
                        }, SymbolKind.NamedType);
                        break;

                    case AnalysisKind.Operation:
                        context.RegisterOperationAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        }, OperationKind.VariableDeclarationGroup);
                        break;

                    case AnalysisKind.OperationBlockStart:
                        context.RegisterOperationBlockStartAction(startContext =>
                        {
                            CallbackFilterSpan = startContext.FilterSpan;
                            CallbackFilterTree = startContext.FilterTree;

                            startContext.RegisterOperationBlockEndAction(endContext =>
                            {
                                if (startContext.FilterTree != endContext.FilterTree)
                                    throw new InvalidOperationException("Mismatched FilterTree");

                                if (startContext.FilterSpan != endContext.FilterSpan)
                                    throw new InvalidOperationException("Mismatched FilterSpan");
                            });
                        });
                        break;

                    case AnalysisKind.OperationBlock:
                        context.RegisterOperationBlockAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        });
                        break;

                    case AnalysisKind.SyntaxNode:
                        context.RegisterSyntaxNodeAction<SyntaxKind>(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        }, SyntaxKind.LocalDeclarationStatement);
                        break;

                    case AnalysisKind.CodeBlockStart:
                        context.RegisterCodeBlockStartAction<SyntaxKind>(startContext =>
                        {
                            CallbackFilterSpan = startContext.FilterSpan;
                            CallbackFilterTree = startContext.FilterTree;

                            startContext.RegisterCodeBlockEndAction(endContext =>
                            {
                                if (startContext.FilterTree != endContext.FilterTree)
                                    throw new InvalidOperationException("Mismatched FilterTree");

                                if (startContext.FilterSpan != endContext.FilterSpan)
                                    throw new InvalidOperationException("Mismatched FilterSpan");
                            });
                        });
                        break;

                    case AnalysisKind.CodeBlock:
                        context.RegisterCodeBlockAction(context =>
                        {
                            CallbackFilterSpan = context.FilterSpan;
                            CallbackFilterTree = context.FilterTree;
                        });
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(_analysisKind);
                }
            }
        }

        [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
        public class MinimumReportedSeverityAnalyzer : DiagnosticAnalyzer
        {
            public static readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor(
                "ID1",
                "Title1",
                "Message1",
                "Category1",
                defaultSeverity: DiagnosticSeverity.Warning,
                isEnabledByDefault: true);

            public DiagnosticSeverity MinimumReportedSeverity { get; private set; }
            public bool AnalyzerInvoked { get; private set; }

            public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(_descriptor);
            public override void Initialize(AnalysisContext context)
            {
                MinimumReportedSeverity = context.MinimumReportedSeverity;
                AnalyzerInvoked = true;
            }
        }
    }
}
