public static void LogError(this GeneratorExecutionContext context, string errorCode, string title, string errorMessage, Location location, string description = "")
        {
            context.LogInfo($"ERROR: {errorCode}, {title}, {errorMessage}");
            var descriptor = new DiagnosticDescriptor(errorCode, title, errorMessage, "Source Generator", DiagnosticSeverity.Error, true, description);

            context.ReportDiagnostic(Diagnostic.Create(descriptor, location));
        }
        public static void WaitForDebuggerAttach(this GeneratorExecutionContext context, string inAssembly = null)
        {
            if (inAssembly != null && !context.Compilation.Assembly.Name.Contains(inAssembly))
            {
                return;
            }

            // Debugger.Launch only works on Windows and not in Rider
            while (!Debugger.IsAttached)
            {
                Task.Delay(500).Wait();
            }

            context.LogInfo($"Debugger attached to assembly: {context.Compilation.Assembly.Name}");
        }
        /// <summary>
        /// Called to perform source generation. A generator can use the context to add source files via the AddSource(String, SourceText) method.
        /// </summary>
        /// <param name="context">The context which provides access to the current compilation and allows manipulation of the output.</param>
        public void Execute(GeneratorExecutionContext context)
        {
            try
            {
                context.LogInfo($"Starting source generation for Assembly=[{context.Compilation.Assembly.Name}]");
                var stopwatch = Stopwatch.StartNew();

                // Scan through the assembly and gather all types which should have property bags generated.
                var containerTypeSymbols = ContainerTypeUtility.GetPropertyContainerTypes(context.Compilation.Assembly);
                var propertyBags         = containerTypeSymbols.Select(containerTypeSymbol => new PropertyBagDefinition(containerTypeSymbol)).ToList();

                if (propertyBags.Count != 0)
                {
                    var namespaceDeclarationSyntax  = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Unity.Properties.Generated"));
                    var usingSystemReflectionSyntax = SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System.Reflection"))
                                                      .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($"#if !{Defines.NET_DOTS}"))
                                                      .WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia("#endif"));

                    foreach (var propertyBag in propertyBags)
                    {
                        if (!propertyBag.IsValidPropertyBag)
                        {
                            var rule = new DiagnosticDescriptor(
                                "SGP002",
                                "Unable to generate PropertyBag",
                                $"Unable to generate PropertyBag for Type=[{propertyBag.ContainerType}]. The type is inaccessible due to its protection level. The type must be flagged as 'public' or 'internal.",
                                "Source Generator",
                                DiagnosticSeverity.Warning,
                                true,
                                string.Empty);

                            context.ReportDiagnostic(Diagnostic.Create(rule, propertyBag.ContainerType.GetSyntaxLocation()));
                            continue;
                        }

                        foreach (var property in propertyBag.GetPropertyMembers().Where(p => !p.IsValidProperty))
                        {
                            var rule = new DiagnosticDescriptor(
                                "SGP003",
                                "Unable to generate Property",
                                $"Unable to generate Property=[{property.PropertyName}] with Type=[{property.MemberType}] for Container=[{propertyBag.ContainerType}]. The member type is inaccessible due to its protection level. The member type must be flagged as 'public' or 'internal.",
                                "Source Generator",
                                DiagnosticSeverity.Warning,
                                true,
                                string.Empty);

                            context.ReportDiagnostic(Diagnostic.Create(rule, propertyBag.ContainerType.GetSyntaxLocation()));
                        }

                        var propertyBagDeclarationSyntax     = PropertyBagFactory.CreatePropertyBagClassDeclarationSyntax(propertyBag);
                        var propertyBagCompilationUnitSyntax = SyntaxFactory.CompilationUnit();

                        if (propertyBag.UsesReflection)
                        {
                            propertyBagCompilationUnitSyntax = propertyBagCompilationUnitSyntax.AddUsings(usingSystemReflectionSyntax);
                        }

                        propertyBagCompilationUnitSyntax = propertyBagCompilationUnitSyntax.AddMembers(namespaceDeclarationSyntax.AddMembers(propertyBagDeclarationSyntax));
                        propertyBagCompilationUnitSyntax = propertyBagCompilationUnitSyntax.NormalizeWhitespace();

                        var propertyBagHint       = propertyBag.PropertyBagClassName;
                        var propertyBagPath       = context.GetGeneratedDebugSourcePath(propertyBagHint);
                        var propertyBagSourceText = propertyBagCompilationUnitSyntax.GetTextUtf8();

                        File.WriteAllText(propertyBagPath, propertyBagSourceText);
                        context.AddSource(propertyBagHint, propertyBagSourceText);
                    }

                    var registryDeclarationSyntax     = PropertyBagRegistryFactory.CreatePropertyBagRegistryClassDeclarationSyntax(propertyBags);
                    var registryCompilationUnitSyntax = SyntaxFactory.CompilationUnit();

                    registryCompilationUnitSyntax = registryCompilationUnitSyntax.AddMembers(namespaceDeclarationSyntax.AddMembers(registryDeclarationSyntax));
                    registryCompilationUnitSyntax = registryCompilationUnitSyntax.NormalizeWhitespace();

                    var propertyBagRegistryHint       = "PropertyBagRegistry";
                    var propertyBagRegistryPath       = context.GetGeneratedDebugSourcePath(propertyBagRegistryHint);
                    var propertyBagRegistrySourceText = registryCompilationUnitSyntax.GetTextUtf8();

                    File.WriteAllText(propertyBagRegistryPath, propertyBagRegistrySourceText);
                    context.AddSource(propertyBagRegistryHint, propertyBagRegistrySourceText);
                }

                context.LogInfo($"Finished source generation for Assembly=[{context.Compilation.Assembly.Name}] with {propertyBags.Count} property bags in {stopwatch.ElapsedMilliseconds}ms");
            }
            catch (Exception exception)
            {
                var rule = new DiagnosticDescriptor(
                    "SGP001",
                    "Unknown Exception",
                    exception.ToString(),
                    "Source Generator",
                    DiagnosticSeverity.Error,
                    true,
                    string.Empty);

                context.ReportDiagnostic(Diagnostic.Create(rule, context.Compilation.SyntaxTrees.First().GetRoot().GetLocation()));
            }
        }