private void PreAnalyzeFunctionData(FunctionData functionData, SemanticModel semanticModel)
        {
            var methodSymbol = functionData.Symbol;

            if (functionData.Conversion == MethodConversion.Ignore)
            {
                return;
            }

            functionData.Conversion = _configuration.GetMethodConversion(methodSymbol);
            // TODO: validate conversion
            if (functionData.Conversion == MethodConversion.Ignore)
            {
                functionData.Ignore(IgnoreReason.MethodConversion, true);
                return;
            }

            functionData.ForceAsync = functionData.Conversion.HasFlag(MethodConversion.ToAsync);
            var newType = functionData.TypeData.IsNewType;

            // Here we want to log only ignored methods that were explicitly set to async
            void IgnoreOrCopy(IgnoreReason reason)
            {
                if (newType)
                {
                    functionData.Copy();
                }
                else
                {
                    if (functionData.ForceAsync)
                    {
                        reason = reason.WithSeverity(DiagnosticSeverity.Warning);
                    }
                    functionData.Ignore(reason);
                }
            }

            if (methodSymbol.IsAsync || methodSymbol.Name.EndsWith("Async"))
            {
                IgnoreOrCopy(IgnoreReason.AlreadyAsync);
                return;
            }
            if (
                methodSymbol.MethodKind != MethodKind.Ordinary &&
                methodSymbol.MethodKind != MethodKind.ExplicitInterfaceImplementation &&
                methodSymbol.MethodKind != MethodKind.PropertyGet &&
                methodSymbol.MethodKind != MethodKind.PropertySet)
            {
                IgnoreOrCopy(IgnoreReason.NotSupported($"Unsupported method kind {methodSymbol.MethodKind}"));
                return;
            }

            if (methodSymbol.Parameters.Any(o => o.RefKind == RefKind.Out))
            {
                IgnoreOrCopy(IgnoreReason.OutParameters);
                return;
            }
            FillFunctionLocks(functionData, semanticModel);

            if (!(functionData is MethodOrAccessorData methodData))
            {
                return;
            }

            // Override user configuration if the method is set to be copied on a partial type conversion
            if (methodData.Conversion == MethodConversion.Copy && !newType)
            {
                methodData.AddDiagnostic(
                    "Method cannot be copied, when the containing type conversion is not set to be a new type. " +
                    $"Override the method conversion to '{MethodConversion.Unknown}'",
                    DiagnosticSeverity.Warning);
                methodData.Conversion = MethodConversion.Unknown;
            }

            // Check if explicitly implements external interfaces
            if (methodSymbol.MethodKind == MethodKind.ExplicitInterfaceImplementation)
            {
                foreach (var interfaceMember in methodSymbol.ExplicitInterfaceImplementations)
                {
                    // Check if the interface member has an async counterpart
                    var asyncConterparts = FillRelatedAsyncMethods(methodData, interfaceMember);
                    if (methodSymbol.ContainingAssembly.Name != interfaceMember.ContainingAssembly.Name)
                    {
                        methodData.ExternalRelatedMethods.TryAdd(interfaceMember);
                        if (!asyncConterparts.Any())
                        {
                            IgnoreOrCopy(IgnoreReason.ExplicitImplementsExternalMethodWithoutAsync(interfaceMember));
                            return;
                        }
                    }
                    else
                    {
                        methodData.ImplementedInterfaces.TryAdd(interfaceMember);
                    }
                    // For new types we need to copy all interface members
                    if (newType)
                    {
                        methodData.SoftCopy();
                    }
                }
            }

            // Check if the method is overriding an external method
            var overridenMethod = methodSymbol.OverriddenMethod;

            while (overridenMethod != null)
            {
                // Check if the member has an async counterpart that is not implemented in the current type (missing member)
                var asyncConterparts = FillRelatedAsyncMethods(methodData, overridenMethod);
                if (methodSymbol.ContainingAssembly.Name != overridenMethod.ContainingAssembly.Name)
                {
                    methodData.ExternalRelatedMethods.TryAdd(overridenMethod);
                    if (!asyncConterparts.Any())
                    {
                        IgnoreOrCopy(IgnoreReason.OverridesExternalMethodWithoutAsync(overridenMethod));
                        return;
                    }
                }
                else
                {
                    methodData.OverridenMethods.TryAdd(overridenMethod);
                }
                if (overridenMethod.OverriddenMethod != null)
                {
                    overridenMethod = overridenMethod.OverriddenMethod;
                }
                else
                {
                    break;
                }
            }
            methodData.BaseOverriddenMethod = overridenMethod;

            // Check if the method is implementing an external interface, if true skip as we cannot modify externals
            // FindImplementationForInterfaceMember will find the first implementation method starting from the deepest base class
            var type             = methodSymbol.ContainingType;
            var interfaceMembers = type.AllInterfaces
                                   .SelectMany(
                o => o.GetMembers(methodSymbol.Name)
                .Where(
                    m =>
            {
                // Find out if the method implements the interface member or an override
                // method that implements it
                var impl = type.FindImplementationForInterfaceMember(m);
                return(methodSymbol.Equals(impl) || methodData.OverridenMethods.Any(ov => ov.Equals(impl)));
            }
                    ))
                                   .OfType <IMethodSymbol>()
                                   .ToList();

            foreach (var interfaceMember in interfaceMembers)
            {
                // Check if the member has an async counterpart that is not implemented in the current type (missing member)
                var asyncConterparts = FillRelatedAsyncMethods(methodData, interfaceMember);
                if (methodSymbol.ContainingAssembly.Name != interfaceMember.ContainingAssembly.Name)
                {
                    // Check if there is an internal interface member that hides this method
                    if (interfaceMembers.Where(o => o.ContainingAssembly.Name == methodSymbol.ContainingAssembly.Name)
                        .Any(o => o.GetHiddenMethods().Any(hm => hm.Equals(interfaceMember))))
                    {
                        continue;
                    }
                    methodData.ExternalRelatedMethods.TryAdd(interfaceMember);
                    if (!asyncConterparts.Any())
                    {
                        IgnoreOrCopy(IgnoreReason.ImplementsExternalMethodWithoutAsync(interfaceMember));
                        return;
                    }
                }
                else
                {
                    methodData.ImplementedInterfaces.TryAdd(interfaceMember);
                }
                // For new types we need to copy all interface member
                if (newType)
                {
                    methodData.SoftCopy();
                }
            }

            // Verify if there is already an async counterpart for this method
            //TODO: this is not correct when generating methods with a cancellation token as here we do not know
            // if the generated method will have the cancellation token parameter or not
            var asyncCounterparts = GetAsyncCounterparts(methodSymbol.OriginalDefinition, _preAnalyzeSearchOptions).ToList();

            if (asyncCounterparts.Any())
            {
                if (!_preAnalyzeSearchOptions.HasFlag(AsyncCounterpartsSearchOptions.HasCancellationToken) && asyncCounterparts.Count > 1)
                {
                    throw new InvalidOperationException($"Method {methodSymbol} has more than one async counterpart");
                }
                // We shall get a maximum of two async counterparts when the HasCancellationToken flag is used
                if (_preAnalyzeSearchOptions.HasFlag(AsyncCounterpartsSearchOptions.HasCancellationToken) && asyncCounterparts.Count > 2)
                {
                    throw new InvalidOperationException($"Method {methodSymbol} has more than two async counterparts");
                }

                foreach (var asyncCounterpart in asyncCounterparts)
                {
                    // Check if the async counterpart has a cancellation token
                    if (asyncCounterpart.Parameters.Length > methodSymbol.Parameters.Length)
                    {
                        methodData.AsyncCounterpartWithTokenSymbol = asyncCounterpart;
                    }
                    else
                    {
                        methodData.AsyncCounterpartSymbol = asyncCounterpart;
                    }

                    methodData.IgnoreAsyncCounterpart = _configuration.IgnoreAsyncCounterpartsPredicates.Any(p => p(asyncCounterpart));
                }
                // TODO: define a better logic
                // We should not ignore if none of the async counterparts is in the sync method type.
                if (asyncCounterparts.Any(o => o.ContainingType.Equals(methodSymbol.ContainingType))

                    /*(_configuration.UseCancellationTokens && asyncCounterparts.Count == 2) ||
                     * (!_configuration.UseCancellationTokens && asyncCounterparts.Count == 1)*/
                    )
                {
                    IgnoreOrCopy(IgnoreReason.AsyncCounterpartExists);
                    methodData.CancellationTokenRequired = methodData.AsyncCounterpartWithTokenSymbol != null;
                    return;
                }
            }

            // Create an override async method if any of the related async methods is virtual or abstract
            // We need to do this here so that the method body will get scanned for async counterparts
            if (methodData.RelatedAsyncMethods.Any(o => o.IsVirtual || o.IsAbstract) &&
                _configuration.CanScanForMissingAsyncMembers?.Invoke(methodData.TypeData.Symbol) == true)
            {
                if (!methodData.Conversion.HasAnyFlag(MethodConversion.ToAsync, MethodConversion.Smart))
                {
                    methodData.Smart();
                }
                if (newType)
                {
                    methodData.SoftCopy();
                }

                // We have to generate the cancellation token parameter if the async member has more parameters that the sync counterpart
                var asyncMember = methodData.RelatedAsyncMethods
                                  .Where(o => o.IsVirtual || o.IsAbstract)
                                  .FirstOrDefault(o => o.Parameters.Length > methodData.Symbol.Parameters.Length);
                if (asyncMember != null)
                {
                    methodData.CancellationTokenRequired = true;
                    // We suppose that the cancellation token is the last parameter
                    methodData.MethodCancellationToken = asyncMember.Parameters.Last().HasExplicitDefaultValue
                                                ? MethodCancellationToken.Optional
                                                : MethodCancellationToken.Required;
                }
            }
        }