private static void Analyze(SymbolAnalysisContext context, FriendClassCompilationData compilation)
        {
            if (context.Symbol is not INamedTypeSymbol symbol || !(symbol.TypeKind is TypeKind.Class or TypeKind.Struct))
            {
                return;
            }

            AttributeData[] attributes = symbol.GetAttributes(compilation.FriendClassAttribute !).ToArray();

            if (attributes.Length == 0)
            {
                if (TryGetInvalidConfigurationDiagnostic(symbol, compilation, out Diagnostic? d))
                {
                    context.ReportDiagnostic(d);
                }

                return;
            }

            FriendClassConfiguration config = GetConfiguration(symbol, compilation);

            if (!ValidateConfiguration(symbol, config, out Diagnostic? diagnostic))
            {
                context.ReportDiagnostic(diagnostic);
            }

            foreach (Diagnostic d in AnalyzeAttributes(attributes, symbol, compilation))
            {
                context.ReportDiagnostic(d);
            }
        }
        private static FriendClassConfiguration GetConfiguration(INamedTypeSymbol symbol, FriendClassCompilationData compilation)
        {
            FriendClassConfiguration @default = FriendClassConfiguration.Default;

            if (symbol.GetAttribute(compilation.FriendClassConfigurationAttribute !) is not AttributeData attr ||
                attr.ApplicationSyntaxReference is null ||
                attr.ApplicationSyntaxReference.GetSyntax() is not AttributeSyntax syntax
                )
            {
                return(@default);
            }

            bool allowsChildren = GetBoolProperty(MemberNames.Config_AllowsChildren, @default.AllowsChildren);

            return(new()
            {
                AllowsChildren = allowsChildren,
                Syntax = syntax
            });

            bool GetBoolProperty(string name, bool @default)
            {
                if (!attr.TryGetNamedArgumentValue(name, out bool value))
                {
                    value = @default;
                }

                return(value);
            }
        }
        private static bool ValidateConfiguration(
            INamedTypeSymbol symbol,
            FriendClassConfiguration configuration,
            [NotNullWhen(false)] out Diagnostic?diagnostic
            )
        {
            if (!configuration.AllowsChildren)
            {
                if (configuration.Syntax is not null)
                {
                    diagnostic = Diagnostic.Create(
                        descriptor: DUR0313_ConfigurationIsRedundant,
                        location: configuration.Syntax.GetLocation(),
                        messageArgs: new[] { symbol }
                        );

                    return(false);
                }
            }
            else if (symbol.TypeKind == TypeKind.Struct || symbol.IsSealed || symbol.IsStatic)
            {
                diagnostic = Diagnostic.Create(
                    descriptor: DUR0311_DoNotAllowChildrenOnSealedType,
                    location: GetArgumentLocation(MemberNames.Config_AllowsChildren),
                    messageArgs: new[] {
                    symbol
                }
                    );

                return(false);
            }

            diagnostic = null;
            return(true);

            Location GetArgumentLocation(string argName)
            {
                if (configuration.Syntax is not null &&
                    configuration.Syntax.ArgumentList is not null &&
                    configuration.Syntax.ArgumentList.Arguments is SeparatedSyntaxList <AttributeArgumentSyntax> {
                    Count: > 0
                } arguments&&
                    arguments.FirstOrDefault(arg => arg.NameEquals is not null && arg.NameEquals.Name.ToString() == argName) is AttributeArgumentSyntax arg
                    )
                {
                    return(arg.GetLocation());
                }

                return(symbol.Locations.FirstOrDefault() ?? Location.None);
            }
        }