private void AnalyzeInvocationExpression(DocumentData documentData, InvocationExpressionSyntax node, BodyFunctionDataReference functionReferenceData)
        {
            var functionData = functionReferenceData.Data;
            var methodSymbol = functionReferenceData.ReferenceSymbol;
            var functionNode = functionData.GetNode();

            if (IgnoreIfInvalidAncestor(node, functionNode, functionReferenceData))
            {
                return;
            }

            // If the invocation returns a Task then we need to analyze it further to see how the Task is handled
            if (methodSymbol.ReturnType.IsTaskType())
            {
                var retrunType   = (INamedTypeSymbol)methodSymbol.ReturnType;
                var canBeAwaited = false;
                var currNode     = node.Parent;
                while (true)
                {
                    var memberExpression = currNode as MemberAccessExpressionSyntax;
                    if (memberExpression == null)
                    {
                        break;
                    }
                    var memberName = memberExpression.Name.ToString();
                    if (retrunType.IsGenericType && memberName == "Result")
                    {
                        canBeAwaited = true;
                        break;
                    }
                    if (memberName == "ConfigureAwait")
                    {
                        var invocationNode = currNode.Parent as InvocationExpressionSyntax;
                        if (invocationNode != null)
                        {
                            functionReferenceData.ConfigureAwaitParameter = invocationNode.ArgumentList.Arguments.First().Expression;
                            currNode = invocationNode.Parent;
                            continue;
                        }
                        break;
                    }
                    if (memberName == "GetAwaiter")
                    {
                        var invocationNode = currNode.Parent as InvocationExpressionSyntax;
                        if (invocationNode != null)
                        {
                            currNode = invocationNode.Parent;
                            continue;
                        }
                        break;
                    }
                    if (_taskResultMethods.Contains(memberName))
                    {
                        var invocationNode = currNode.Parent as InvocationExpressionSyntax;
                        if (invocationNode != null)
                        {
                            canBeAwaited = true;
                        }
                    }
                    break;
                }
                if (!canBeAwaited)
                {
                    functionReferenceData.AwaitInvocation = false;
                    functionReferenceData.AddDiagnostic("Cannot await invocation that returns a Task without being synchronously awaited", DiagnosticSeverity.Info);
                }
                else
                {
                    functionReferenceData.SynchronouslyAwaited = true;
                }
            }
            if (node.Expression is SimpleNameSyntax)
            {
                functionReferenceData.InvokedFromType = functionData.Symbol.ContainingType;
            }
            else if (node.Expression is MemberAccessExpressionSyntax memberAccessExpression)
            {
                functionReferenceData.InvokedFromType = documentData.SemanticModel.GetTypeInfo(memberAccessExpression.Expression).Type;
            }

            FindAsyncCounterparts(functionReferenceData);

            var delegateParams = methodSymbol.Parameters.Select(o => o.Type.TypeKind == TypeKind.Delegate).ToList();

            for (var i = 0; i < node.ArgumentList.Arguments.Count; i++)
            {
                var argument           = node.ArgumentList.Arguments[i];
                var argumentExpression = argument.Expression;
                // We have to process anonymous funcions as they will not be analyzed as arguments
                if (argumentExpression.IsFunction())
                {
                    var anonFunction = (AnonymousFunctionData)functionData.ChildFunctions[argumentExpression];
                    functionReferenceData.AddDelegateArgument(new DelegateArgumentData(anonFunction, i));
                    anonFunction.ArgumentOfFunctionInvocation = functionReferenceData;
                    continue;
                }
                if (argumentExpression.IsKind(SyntaxKind.IdentifierName) ||
                    argumentExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression))
                {
                    var argRefFunction = functionData.BodyFunctionReferences.FirstOrDefault(o => argument.Equals(o.ReferenceNode));
                    if (argRefFunction == null)
                    {
                        // Ignore only if the async argument does not match
                        // TODO: internal methods, unify with CalculateFunctionArguments
                        if (functionReferenceData.ReferenceFunctionData == null && delegateParams[i])                         // If the parameter is a delegate check the symbol of the argument
                        {
                            var argSymbol = documentData.SemanticModel.GetSymbolInfo(argumentExpression).Symbol;
                            if (argSymbol is ILocalSymbol arglocalSymbol)
                            {
                                // TODO: local arguments
                                functionReferenceData.Ignore(IgnoreReason.NotSupported("Local delegate arguments are currently not supported"));
                                return;
                            }
                            if (argSymbol is IMethodSymbol argMethodSymbol)
                            {
                                // TODO: support custom async counterparts that have different parameters
                                // If the invocation has at least one argument that does not fit into any async counterparts we have to ignore it
                                if (functionReferenceData.ReferenceAsyncSymbols
                                    .Where(o => o.Parameters.Length >= methodSymbol.Parameters.Length)                                     // The async counterpart may have less parameters. e.g. Parallel.For -> Task.WhenAll
                                    .All(o => !((IMethodSymbol)o.Parameters[i].Type.GetMembers("Invoke").First()).ReturnType.Equals(argMethodSymbol.ReturnType)))
                                {
                                    functionReferenceData.Ignore(IgnoreReason.Custom("The delegate argument does not fit to any async counterparts", DiagnosticSeverity.Hidden));
                                    return;
                                }
                            }
                        }
                        continue;
                    }
                    functionReferenceData.AddDelegateArgument(new DelegateArgumentData(argRefFunction, i));
                    argRefFunction.ArgumentOfFunctionInvocation = functionReferenceData;
                }
            }

            SetAsyncCounterpart(functionReferenceData);

            CalculateLastInvocation(node, functionReferenceData);

            foreach (var analyzer in _configuration.InvocationExpressionAnalyzers)
            {
                analyzer.AnalyzeInvocationExpression(node, functionReferenceData, documentData.SemanticModel);
            }

            PropagateCancellationToken(functionReferenceData);
        }
        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;
                }
            }
        }
        private void AnalyzeMethodReference(DocumentData documentData, BodyFunctionDataReference refData)
        {
            var nameNode = refData.ReferenceNameNode;

            // Find the actual usage of the method
            SyntaxNode currNode = nameNode;
            var        ascend   = true;

            if (refData.ReferenceSymbol.IsPropertyAccessor())
            {
                ascend = false;
                AnalyzeAccessor(documentData, nameNode, refData);
            }
            else
            {
                currNode = nameNode.Parent;
            }

            while (ascend)
            {
                ascend = false;
                switch (currNode.Kind())
                {
                case SyntaxKind.ConditionalExpression:
                    break;

                case SyntaxKind.InvocationExpression:
                    AnalyzeInvocationExpression(documentData, (InvocationExpressionSyntax)currNode, refData);
                    break;

                case SyntaxKind.Argument:
                    AnalyzeArgumentExpression((ArgumentSyntax)currNode, nameNode, refData);
                    break;

                case SyntaxKind.AddAssignmentExpression:
                    refData.Ignore(IgnoreReason.Custom(
                                       $"Cannot attach an async method to an event (void async is not an option as cannot be awaited)", DiagnosticSeverity.Info));
                    break;

                case SyntaxKind.SubtractAssignmentExpression:
                    refData.Ignore(IgnoreReason.Custom($"Cannot detach an async method to an event", DiagnosticSeverity.Info));
                    break;

                case SyntaxKind.VariableDeclaration:
                    refData.Ignore(IgnoreReason.NotSupported($"Assigning async method to a variable is not supported"));
                    break;

                case SyntaxKind.CastExpression:
                    refData.AwaitInvocation = true;
                    ascend = true;
                    break;

                case SyntaxKind.ReturnStatement:
                    break;

                case SyntaxKind.ArrayInitializerExpression:
                case SyntaxKind.CollectionInitializerExpression:
                case SyntaxKind.ComplexElementInitializerExpression:
                    refData.Ignore(IgnoreReason.NotSupported($"Async method inside an array/collection initializer is not supported"));
                    break;

                // skip
                case SyntaxKind.VariableDeclarator:
                case SyntaxKind.EqualsValueClause:
                case SyntaxKind.SimpleMemberAccessExpression:
                case SyntaxKind.ArgumentList:
                case SyntaxKind.ObjectCreationExpression:
                case SyntaxKind.MemberBindingExpression:                         // ?.
                    ascend = true;
                    break;

                default:
                    throw new NotSupportedException(
                              $"Unknown node kind: {currNode.Kind()} at {currNode?.SyntaxTree.GetLineSpan(currNode.Span)}. Node:{Environment.NewLine}{currNode}");
                }

                if (ascend)
                {
                    currNode = currNode.Parent;
                }
            }
            refData.ReferenceNode = currNode;
            if (!refData.AwaitInvocation.HasValue)
            {
                refData.AwaitInvocation = refData.Conversion != ReferenceConversion.Ignore;
            }
        }