File: src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CodeGeneration\ConstructorGenerator.cs
Web Access
Project: src\src\CodeStyle\CSharp\CodeFixes\Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.CSharp.CodeStyle.Fixes)
// 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.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
 
namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration;
 
using static CodeGenerationHelpers;
using static CSharpCodeGenerationHelpers;
using static CSharpSyntaxTokens;
using static SyntaxFactory;
 
internal static class ConstructorGenerator
{
    private static MemberDeclarationSyntax? LastConstructorOrField(SyntaxList<MemberDeclarationSyntax> members)
        => LastConstructor(members) ?? LastField(members);
 
    internal static TypeDeclarationSyntax AddConstructorTo(
        TypeDeclarationSyntax destination,
        IMethodSymbol constructor,
        CSharpCodeGenerationContextInfo info,
        IList<bool>? availableIndices,
        CancellationToken cancellationToken)
    {
        var constructorDeclaration = GenerateConstructorDeclaration(constructor, info, cancellationToken);
 
        // Generate after the last constructor, or after the last field, or at the start of the
        // type.
        var members = Insert(destination.Members, constructorDeclaration, info,
            availableIndices, after: LastConstructorOrField, before: FirstMember);
 
        return AddMembersTo(destination, members, cancellationToken);
    }
 
    internal static ConstructorDeclarationSyntax GenerateConstructorDeclaration(
        IMethodSymbol constructor,
        CSharpCodeGenerationContextInfo info,
        CancellationToken cancellationToken)
    {
        var reusableSyntax = GetReuseableSyntaxNodeForSymbol<ConstructorDeclarationSyntax>(constructor, info);
        if (reusableSyntax != null)
        {
            return reusableSyntax;
        }
 
        var hasNoBody = !info.Context.GenerateMethodBodies;
 
        var declaration = ConstructorDeclaration(
            attributeLists: AttributeGenerator.GenerateAttributeLists(constructor.GetAttributes(), info),
            modifiers: GenerateModifiers(constructor, info),
            identifier: CodeGenerationConstructorInfo.GetTypeName(constructor).ToIdentifierToken(),
            parameterList: ParameterGenerator.GenerateParameterList(constructor.Parameters, isExplicit: false, info: info),
            initializer: GenerateConstructorInitializer(constructor),
            body: hasNoBody ? null : GenerateBlock(constructor),
            semicolonToken: hasNoBody ? SemicolonToken : default);
 
        declaration = UseExpressionBodyIfDesired(info, declaration, cancellationToken);
 
        return AddFormatterAndCodeGeneratorAnnotationsTo(
            ConditionallyAddDocumentationCommentTo(declaration, constructor, info, cancellationToken));
    }
 
    private static ConstructorDeclarationSyntax UseExpressionBodyIfDesired(
        CSharpCodeGenerationContextInfo info, ConstructorDeclarationSyntax declaration, CancellationToken cancellationToken)
    {
        if (declaration.ExpressionBody == null)
        {
            if (declaration.Body?.TryConvertToArrowExpressionBody(
                declaration.Kind(), info.LanguageVersion, info.Options.PreferExpressionBodiedConstructors.Value, cancellationToken,
                out var expressionBody, out var semicolonToken) == true)
            {
                return declaration.WithBody(null)
                                  .WithExpressionBody(expressionBody)
                                  .WithSemicolonToken(semicolonToken);
            }
        }
 
        return declaration;
    }
 
    private static ConstructorInitializerSyntax? GenerateConstructorInitializer(
        IMethodSymbol constructor)
    {
        var thisArguments = CodeGenerationConstructorInfo.GetThisConstructorArgumentsOpt(constructor);
 
        var arguments = !thisArguments.IsDefault ? thisArguments : CodeGenerationConstructorInfo.GetBaseConstructorArgumentsOpt(constructor);
        var kind = CodeGenerationConstructorInfo.GetThisConstructorArgumentsOpt(constructor) != null
            ? SyntaxKind.ThisConstructorInitializer
            : SyntaxKind.BaseConstructorInitializer;
 
        return arguments == null
            ? null
            : ConstructorInitializer(kind).WithArgumentList(GenerateArgumentList(arguments));
    }
 
    private static ArgumentListSyntax GenerateArgumentList(ImmutableArray<SyntaxNode> arguments)
        => ArgumentList([.. arguments.Select(ArgumentGenerator.GenerateArgument)]);
 
    private static BlockSyntax GenerateBlock(
        IMethodSymbol constructor)
    {
        var statements = CodeGenerationConstructorInfo.GetStatements(constructor) == null
            ? default
            : StatementGenerator.GenerateStatements(CodeGenerationConstructorInfo.GetStatements(constructor));
 
        return Block(statements);
    }
 
    private static SyntaxTokenList GenerateModifiers(IMethodSymbol constructor, CSharpCodeGenerationContextInfo info)
    {
        using var _ = ArrayBuilder<SyntaxToken>.GetInstance(out var tokens);
 
        if (constructor.IsStatic)
        {
            tokens.Add(StaticKeyword);
        }
        else
        {
            AddAccessibilityModifiers(constructor.DeclaredAccessibility, tokens, info, Accessibility.Private);
        }
 
        if (CodeGenerationConstructorInfo.GetIsUnsafe(constructor))
            tokens.Add(UnsafeKeyword);
 
        return [.. tokens];
    }
}