File: src\Analyzers\Core\CodeFixes\GenerateDefaultConstructors\AbstractGenerateDefaultConstructorsService.AbstractCodeAction.cs
Web Access
Project: src\src\CodeStyle\Core\CodeFixes\Microsoft.CodeAnalysis.CodeStyle.Fixes.csproj (Microsoft.CodeAnalysis.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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
#if CODE_STYLE
using DeclarationModifiers = Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers;
#else
using DeclarationModifiers = Microsoft.CodeAnalysis.Editing.DeclarationModifiers;
#endif
 
namespace Microsoft.CodeAnalysis.GenerateDefaultConstructors;
 
internal abstract partial class AbstractGenerateDefaultConstructorsService<TService>
{
    private abstract class AbstractCodeAction : CodeAction
    {
        private readonly IList<IMethodSymbol> _constructors;
        private readonly Document _document;
        private readonly State _state;
        private readonly string _title;
 
        protected AbstractCodeAction(
            Document document,
            State state,
            IList<IMethodSymbol> constructors,
            string title)
        {
            _document = document;
            _state = state;
            _constructors = constructors;
            _title = title;
        }
 
        public override string Title => _title;
 
        protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(_state.ClassType);
            var result = await CodeGenerator.AddMemberDeclarationsAsync(
                new CodeGenerationSolutionContext(
                    _document.Project.Solution,
                    CodeGenerationContext.Default),
                _state.ClassType,
                _constructors.Select(CreateConstructorDefinition),
                cancellationToken).ConfigureAwait(false);
 
            return result;
        }
 
        private IMethodSymbol CreateConstructorDefinition(
            IMethodSymbol baseConstructor)
        {
            var syntaxFactory = _document.GetRequiredLanguageService<SyntaxGenerator>();
            var baseConstructorArguments = baseConstructor.Parameters.Length != 0
                ? syntaxFactory.CreateArguments(baseConstructor.Parameters)
                : default;
 
            var classType = _state.ClassType;
            Contract.ThrowIfNull(classType);
 
            var accessibility = DetermineAccessibility(baseConstructor, classType);
            return CodeGenerationSymbolFactory.CreateConstructorSymbol(
                attributes: default,
                accessibility: accessibility,
                modifiers: new DeclarationModifiers(),
                typeName: classType.Name,
                parameters: baseConstructor.Parameters,
                statements: default,
                baseConstructorArguments: baseConstructorArguments);
        }
 
        private static Accessibility DetermineAccessibility(IMethodSymbol baseConstructor, INamedTypeSymbol classType)
        {
            // If our base is abstract, and we are not, then (since we likely want to be
            // instantiated) we make our constructor public by default.
            if (baseConstructor.ContainingType.IsAbstractClass() && !classType.IsAbstractClass())
                return Accessibility.Public;
 
            // If our base constructor is public, and we're abstract, we switch to being
            // protected as that's a more natural default for constructors in abstract classes.
            if (classType.IsAbstractClass() && baseConstructor.DeclaredAccessibility == Accessibility.Public)
                return Accessibility.Protected;
 
            if (classType.IsSealed)
            {
                // remove protected as it makes no sense in a sealed type.
                switch (baseConstructor.DeclaredAccessibility)
                {
                    case Accessibility.Protected:
                        return Accessibility.Public;
                    case Accessibility.ProtectedAndInternal:
                    case Accessibility.ProtectedOrInternal:
                        return Accessibility.Internal;
                }
            }
 
            // Defer to whatever the base constructor was declared as.
            return baseConstructor.DeclaredAccessibility;
        }
    }
}