private static bool TryAddBuildMethod(GeneratorExecutionContext context, BuilderToGenerate builder, ref ClassDeclarationSyntax builderClass)
        {
            var objectType            = ParseTypeName(builder.Name);
            var buildMethodStatements = new List <StatementSyntax>();

            var creationExpression          = ObjectCreationExpression(objectType);
            var propertiesSetViaConstructor = new List <PropertyToGenerate>();

            if (builder.ConstructorToUse is { } constructorToUse)
            {
                var arguments = new ArgumentSyntax[constructorToUse.Parameters.Length];
                var blocked   = false;
                for (var i = 0; i < constructorToUse.Parameters.Length; i++)
                {
                    var parameterName    = constructorToUse.Parameters[i].Name;
                    var matchingProperty = builder.Properties.FirstOrDefault(p =>
                                                                             p.PropertyName.Equals(parameterName, StringComparison.OrdinalIgnoreCase));
                    if (matchingProperty == null)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(CannotInferBuilderPropertyFromArgumentDiagnostic,
                                                                   constructorToUse.Parameters[i].Locations[0]));
                        blocked = true;
                    }
                    else
                    {
                        arguments[i] = Argument(PropertyAccessAndDefaultingExpression(matchingProperty));
                        propertiesSetViaConstructor.Add(matchingProperty);
                    }

                    foreach (var property in propertiesSetViaConstructor)
                    {
                        if (property.IsReferenceType && !property.IsNullable)
                        {
                            var throwStatement = ThrowStatement(ObjectCreationExpression(ParseTypeName("System.InvalidOperationException"))
                                                                .AddArgumentListArguments(Argument(LiteralExpression(SyntaxKind.StringLiteralExpression,
                                                                                                                     Literal($"No value present for required property '{property.PropertyName}'.")))));
                            buildMethodStatements.Add(IfStatement(NullCheck(IdentifierName(property.FieldName)), throwStatement));
                        }
                    }
                }

                if (blocked)
                {
                    return(false);
                }

                creationExpression = creationExpression.AddArgumentListArguments(arguments);
            }
        private static ClassDeclarationSyntax AddMutationMethods(BuilderToGenerate builder, ClassDeclarationSyntax builderClass)
        {
            foreach (var property in builder.Properties)
            {
                var lowerCamelParameterName = property.PropertyName.Substring(0, 1).ToLowerInvariant() + property.PropertyName.Substring(1);
                var upperCamelParameterName = property.PropertyName.Substring(0, 1).ToUpperInvariant() + property.PropertyName.Substring(1);

                var localBuilderIdentifier = Identifier("mutatedBuilder");
                builderClass = builderClass.AddMembers(
                    MethodDeclaration(builder.BuilderTypeSyntax, "With" + upperCamelParameterName)
                    .AddModifiers(Token(SyntaxKind.PublicKeyword))
                    .AddParameterListParameters(Parameter(Identifier(lowerCamelParameterName)).WithType(property.TypeSyntax))
                    .AddBodyStatements(
                        LocalDeclarationStatement(VariableDeclaration(IdentifierName("var"))
                                                  .AddVariables(VariableDeclarator(localBuilderIdentifier)
                                                                .WithInitializer(EqualsValueClause(ObjectCreationExpression(builder.BuilderTypeSyntax)
                                                                                                   .AddArgumentListArguments(Argument(ThisExpression())))))),
                        ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
                                                                 MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                                                                        IdentifierName(localBuilderIdentifier), IdentifierName(property.FieldName)),
                                                                 IdentifierName(lowerCamelParameterName))),
                        ReturnStatement(IdentifierName(localBuilderIdentifier))
                        )
                    );

                if (property.IsNullable || property.TypeSyntax is NullableTypeSyntax)
                {
                    builderClass = builderClass.AddMembers(
                        MethodDeclaration(builder.BuilderTypeSyntax, "Without" + upperCamelParameterName)
                        .AddModifiers(Token(SyntaxKind.PublicKeyword))
                        .AddBodyStatements(
                            LocalDeclarationStatement(VariableDeclaration(IdentifierName("var"))
                                                      .AddVariables(VariableDeclarator(localBuilderIdentifier)
                                                                    .WithInitializer(EqualsValueClause(ObjectCreationExpression(builder.BuilderTypeSyntax)
                                                                                                       .AddArgumentListArguments(Argument(ThisExpression())))))),
                            ExpressionStatement(AssignmentExpression(SyntaxKind.SimpleAssignmentExpression,
                                                                     MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
                                                                                            IdentifierName(localBuilderIdentifier), IdentifierName(property.FieldName)),
                                                                     LiteralExpression(SyntaxKind.NullLiteralExpression))),
                            ReturnStatement(IdentifierName(localBuilderIdentifier))
                            )
                        );
                }
            }

            return(builderClass);
        }
        private static Dictionary <string, BuilderToGenerate> DetermineBuildersToGenerate(GeneratorExecutionContext context, INamedTypeSymbol generateDataBuilderAttributeType)
        {
            var builders = new Dictionary <string, BuilderToGenerate>();

            foreach (var inputDocument in context.Compilation.SyntaxTrees)
            {
                context.CancellationToken.ThrowIfCancellationRequested();

                var typeNodes = inputDocument.GetRoot()
                                .DescendantNodesAndSelf(n =>
                                                        n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax || n is TypeDeclarationSyntax)
                                .OfType <TypeDeclarationSyntax>();

                var semanticModel = context.Compilation.GetSemanticModel(inputDocument);

                foreach (var typeNode in typeNodes)
                {
                    if (!typeNode.AttributeLists.ContainsAttributeType(semanticModel, generateDataBuilderAttributeType,
                                                                       exactMatch: true))
                    {
                        continue;
                    }

                    var typeSymbol = semanticModel.GetDeclaredSymbol(typeNode);

                    if (typeSymbol == null)
                    {
                        continue;
                    }

                    var generationBlocked = false;

                    if (typeSymbol.IsAbstract)
                    {
                        context.ReportDiagnostic(Diagnostic.Create(GeneratorOnAbstractDiagnostic, typeSymbol.Locations.First(),
                                                                   typeSymbol.Name));
                        generationBlocked = true;
                    }

                    if (typeNode.Modifiers.Any(m => m.Kind() == SyntaxKind.PartialKeyword))
                    {
                        context.ReportDiagnostic(Diagnostic.Create(GeneratorOnPartialDiagnostic, typeNode.GetLocation(),
                                                                   typeSymbol.Name));
                        generationBlocked = true;
                    }

                    if (generationBlocked)
                    {
                        continue;
                    }

                    string typeFullMetadataName = typeSymbol.GetFullMetadataName();
                    if (!builders.TryGetValue(typeFullMetadataName, out var builder))
                    {
                        builder = new BuilderToGenerate(
                            typeSymbol.ContainingNamespace?.GetFullMetadataName(),
                            typeSymbol.Name,
                            typeNode,
                            typeSymbol.DeclaredAccessibility);

                        IMethodSymbol?constructorToUse = null;

                        foreach (var typeSymbolConstructor in typeSymbol.Constructors)
                        {
                            if (typeSymbolConstructor.IsStatic)
                            {
                                continue;
                            }

                            if (typeSymbolConstructor.Parameters.Length > (constructorToUse?.Parameters.Length ?? 0))
                            {
                                constructorToUse = typeSymbolConstructor;
                            }
                        }

                        builder.ConstructorToUse = constructorToUse;

                        builders.Add(typeFullMetadataName, builder);
                    }

                    builder.Properties.AddRange(
                        typeSymbol
                        .GetMembers()
                        .Concat(typeSymbol.GetBaseTypes().SelectMany(t => t.GetMembers()))
                        .OfType <IPropertySymbol>()
                        .Where(m => m.DeclaredAccessibility == Accessibility.Public && !m.IsReadOnly && !m.IsIndexer && !m.IsStatic)
                        .Select(property => new PropertyToGenerate(
                                    $"_{property.Name.Substring(0, 1).ToLower()}{property.Name.Substring(1)}",
                                    property.Name,
                                    property.Type,
                                    DetermineNullability(semanticModel, property)))
                        );
                }
            }

            return(builders);
        }