internal abstract IMethodSymbol GetDelegatingConstructor()

in src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.GenerateNamedType.cs [19:359]


        internal abstract IMethodSymbol GetDelegatingConstructor(
            SemanticDocument document,
            TObjectCreationExpressionSyntax objectCreation,
            INamedTypeSymbol namedType,
            ISet<IMethodSymbol> candidates,
            CancellationToken cancellationToken);

        private partial class Editor
        {
            private INamedTypeSymbol GenerateNamedType()
            {
                return CodeGenerationSymbolFactory.CreateNamedTypeSymbol(
                    DetermineAttributes(),
                    DetermineAccessibility(),
                    DetermineModifiers(),
                    DetermineTypeKind(),
                    DetermineName(),
                    DetermineTypeParameters(),
                    DetermineBaseType(),
                    DetermineInterfaces(),
                    members: DetermineMembers());
            }

            private INamedTypeSymbol GenerateNamedType(GenerateTypeOptionsResult options)
            {
                if (options.TypeKind == TypeKind.Delegate)
                {
                    return CodeGenerationSymbolFactory.CreateDelegateTypeSymbol(
                        DetermineAttributes(),
                        options.Accessibility,
                        DetermineModifiers(),
                        DetermineReturnType(options),
                        RefKind.None,
                        name: options.TypeName,
                        typeParameters: DetermineTypeParameters(options),
                        parameters: DetermineParameters(options));
                }

                return CodeGenerationSymbolFactory.CreateNamedTypeSymbol(
                    DetermineAttributes(),
                    options.Accessibility,
                    DetermineModifiers(),
                    options.TypeKind,
                    options.TypeName,
                    DetermineTypeParameters(),
                    DetermineBaseType(),
                    DetermineInterfaces(),
                    members: DetermineMembers(options));
            }

            private ITypeSymbol DetermineReturnType(GenerateTypeOptionsResult options)
            {
                if (_state.DelegateMethodSymbol == null ||
                    _state.DelegateMethodSymbol.ReturnType == null ||
                    _state.DelegateMethodSymbol.ReturnType is IErrorTypeSymbol)
                {
                    // Since we cannot determine the return type, we are returning void
                    return _state.Compilation.GetSpecialType(SpecialType.System_Void);
                }
                else
                {
                    return _state.DelegateMethodSymbol.ReturnType;
                }
            }

            private ImmutableArray<ITypeParameterSymbol> DetermineTypeParameters(GenerateTypeOptionsResult options)
            {
                if (_state.DelegateMethodSymbol != null)
                {
                    return _state.DelegateMethodSymbol.TypeParameters;
                }

                // If the delegate symbol cannot be determined then 
                return DetermineTypeParameters();
            }

            private ImmutableArray<IParameterSymbol> DetermineParameters(GenerateTypeOptionsResult options)
            {
                if (_state.DelegateMethodSymbol != null)
                {
                    return _state.DelegateMethodSymbol.Parameters;
                }

                return default;
            }

            private ImmutableArray<ISymbol> DetermineMembers(GenerateTypeOptionsResult options = null)
            {
                var members = ArrayBuilder<ISymbol>.GetInstance();
                AddMembers(members, options);

                if (_state.IsException)
                {
                    AddExceptionConstructors(members);
                }

                return members.ToImmutableAndFree();
            }

            private void AddMembers(ArrayBuilder<ISymbol> members, GenerateTypeOptionsResult options = null)
            {
                AddProperties(members);
                if (!_service.TryGetArgumentList(_state.ObjectCreationExpressionOpt, out var argumentList))
                {
                    return;
                }

                var parameterTypes = GetArgumentTypes(argumentList);

                // Don't generate this constructor if it would conflict with a default exception
                // constructor.  Default exception constructors will be added automatically by our
                // caller.
                if (_state.IsException &&
                    _state.BaseTypeOrInterfaceOpt.InstanceConstructors.Any(
                        c => c.Parameters.Select(p => p.Type).SequenceEqual(parameterTypes)))
                {
                    return;
                }

                // If there's an accessible base constructor that would accept these types, then
                // just call into that instead of generating fields.
                if (_state.BaseTypeOrInterfaceOpt != null)
                {
                    if (_state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface && argumentList.Count == 0)
                    {
                        // No need to add the default constructor if our base type is going to be
                        // 'object'.  We get that constructor for free.
                        return;
                    }

                    var accessibleInstanceConstructors = _state.BaseTypeOrInterfaceOpt.InstanceConstructors.Where(
                        IsSymbolAccessible).ToSet();

                    if (accessibleInstanceConstructors.Any())
                    {
                        var delegatedConstructor = _service.GetDelegatingConstructor(
                            _document,
                            _state.ObjectCreationExpressionOpt,
                            _state.BaseTypeOrInterfaceOpt,
                            accessibleInstanceConstructors,
                            _cancellationToken);
                        if (delegatedConstructor != null)
                        {
                            // There was a best match.  Call it directly.  
                            AddBaseDelegatingConstructor(delegatedConstructor, members);
                            return;
                        }
                    }
                }

                // Otherwise, just generate a normal constructor that assigns any provided
                // parameters into fields.
                AddFieldDelegatingConstructor(argumentList, members, options);
            }

            private void AddProperties(ArrayBuilder<ISymbol> members)
            {
                var typeInference = _document.Project.LanguageServices.GetService<ITypeInferenceService>();
                foreach (var property in _state.PropertiesToGenerate)
                {
                    if (_service.TryGenerateProperty(property, _document.SemanticModel, typeInference, _cancellationToken, out var generatedProperty))
                    {
                        members.Add(generatedProperty);
                    }
                }
            }

            private void AddBaseDelegatingConstructor(
                IMethodSymbol methodSymbol,
                ArrayBuilder<ISymbol> members)
            {
                // If we're generating a constructor to delegate into the no-param base constructor
                // then we can just elide the constructor entirely.
                if (methodSymbol.Parameters.Length == 0)
                {
                    return;
                }

                var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
                members.Add(factory.CreateBaseDelegatingConstructor(
                    methodSymbol, DetermineName()));
            }

            private void AddFieldDelegatingConstructor(
                IList<TArgumentSyntax> argumentList, ArrayBuilder<ISymbol> members, GenerateTypeOptionsResult options = null)
            {
                var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
                var syntaxFactsService = _document.Project.LanguageServices.GetService<ISyntaxFactsService>();

                var availableTypeParameters = _service.GetAvailableTypeParameters(_state, _document.SemanticModel, _intoNamespace, _cancellationToken);
                var parameterTypes = GetArgumentTypes(argumentList);
                var parameterNames = _service.GenerateParameterNames(_document.SemanticModel, argumentList, _cancellationToken);
                var parameters = ArrayBuilder<IParameterSymbol>.GetInstance();

                var parameterToExistingFieldMap = new Dictionary<string, ISymbol>();
                var parameterToNewFieldMap = new Dictionary<string, string>();

                var syntaxFacts = _document.Project.LanguageServices.GetService<ISyntaxFactsService>();
                for (var i = 0; i < parameterNames.Count; i++)
                {
                    var refKind = syntaxFacts.GetRefKindOfArgument(argumentList[i]);

                    var parameterName = parameterNames[i];
                    var parameterType = parameterTypes[i];
                    parameterType = parameterType.RemoveUnavailableTypeParameters(
                        _document.SemanticModel.Compilation, availableTypeParameters);

                    if (!TryFindMatchingField(parameterName, parameterType, parameterToExistingFieldMap, caseSensitive: true))
                    {
                        if (!TryFindMatchingField(parameterName, parameterType, parameterToExistingFieldMap, caseSensitive: false))
                        {
                            parameterToNewFieldMap[parameterName.BestNameForParameter] = parameterName.NameBasedOnArgument;
                        }
                    }

                    parameters.Add(CodeGenerationSymbolFactory.CreateParameterSymbol(
                        attributes: default,
                        refKind: refKind,
                        isParams: false,
                        type: parameterType,
                        name: parameterName.BestNameForParameter));
                }

                // Empty Constructor for Struct is not allowed
                if (!(parameters.Count == 0 && options != null && (options.TypeKind == TypeKind.Struct || options.TypeKind == TypeKind.Structure)))
                {
                    var (fields, constructor) = factory.CreateFieldDelegatingConstructor(
                        _document.SemanticModel.Compilation,
                        DetermineName(), null, parameters.ToImmutable(),
                        parameterToExistingFieldMap, parameterToNewFieldMap,
                        addNullChecks: false, preferThrowExpression: false,
                        cancellationToken: _cancellationToken);
                    members.AddRange(fields);
                    members.Add(constructor);
                }

                parameters.Free();
            }

            private void AddExceptionConstructors(ArrayBuilder<ISymbol> members)
            {
                var factory = _document.Project.LanguageServices.GetService<SyntaxGenerator>();
                var exceptionType = _document.SemanticModel.Compilation.ExceptionType();
                var constructors =
                   exceptionType.InstanceConstructors
                       .Where(c => c.DeclaredAccessibility == Accessibility.Public || c.DeclaredAccessibility == Accessibility.Protected)
                       .Select(c => CodeGenerationSymbolFactory.CreateConstructorSymbol(
                           attributes: default,
                           accessibility: c.DeclaredAccessibility,
                           modifiers: default,
                           typeName: DetermineName(),
                           parameters: c.Parameters,
                           statements: default,
                           baseConstructorArguments: c.Parameters.Length == 0
                                ? default
                                : factory.CreateArguments(c.Parameters)));
                members.AddRange(constructors);
            }

            private ImmutableArray<AttributeData> DetermineAttributes()
            {
                if (_state.IsException)
                {
                    var serializableType = _document.SemanticModel.Compilation.SerializableAttributeType();
                    if (serializableType != null)
                    {
                        var attribute = CodeGenerationSymbolFactory.CreateAttributeData(serializableType);
                        return ImmutableArray.Create(attribute);
                    }
                }

                return default;
            }

            private Accessibility DetermineAccessibility()
            {
                return _service.GetAccessibility(_state, _document.SemanticModel, _intoNamespace, _cancellationToken);
            }

            private DeclarationModifiers DetermineModifiers()
            {
                return default;
            }

            private INamedTypeSymbol DetermineBaseType()
            {
                if (_state.BaseTypeOrInterfaceOpt == null || _state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface)
                {
                    return null;
                }

                return RemoveUnavailableTypeParameters(_state.BaseTypeOrInterfaceOpt);
            }

            private ImmutableArray<INamedTypeSymbol> DetermineInterfaces()
            {
                if (_state.BaseTypeOrInterfaceOpt != null && _state.BaseTypeOrInterfaceOpt.TypeKind == TypeKind.Interface)
                {
                    var type = RemoveUnavailableTypeParameters(_state.BaseTypeOrInterfaceOpt);
                    if (type != null)
                    {
                        return ImmutableArray.Create(type);
                    }
                }

                return ImmutableArray<INamedTypeSymbol>.Empty;
            }

            private INamedTypeSymbol RemoveUnavailableTypeParameters(INamedTypeSymbol type)
            {
                return type.RemoveUnavailableTypeParameters(
                    _document.SemanticModel.Compilation, GetAvailableTypeParameters()) as INamedTypeSymbol;
            }

            private string DetermineName()
            {
                return GetTypeName(_state);
            }

            private ImmutableArray<ITypeParameterSymbol> DetermineTypeParameters()
                => _service.GetTypeParameters(_state, _document.SemanticModel, _cancellationToken);

            private TypeKind DetermineTypeKind()
            {
                return _state.IsStruct
                    ? TypeKind.Struct
                    : _state.IsInterface
                        ? TypeKind.Interface
                        : TypeKind.Class;
            }

            protected IList<ITypeParameterSymbol> GetAvailableTypeParameters()
            {
                var availableInnerTypeParameters = _service.GetTypeParameters(_state, _document.SemanticModel, _cancellationToken);
                var availableOuterTypeParameters = !_intoNamespace && _state.TypeToGenerateInOpt != null
                    ? _state.TypeToGenerateInOpt.GetAllTypeParameters()
                    : SpecializedCollections.EmptyEnumerable<ITypeParameterSymbol>();

                return availableOuterTypeParameters.Concat(availableInnerTypeParameters).ToList();
            }
        }