private static bool ParentIsElementAccessOrForEachExpression(InvocationExpressionSyntax invocation)
        {
            if (invocation.IsParentKind(SyntaxKind.ElementAccessExpression))
            {
                return(true);
            }

            if (invocation.IsParentKind(SyntaxKind.ForEachStatement))
            {
                var forEachStatement = (ForEachStatementSyntax)invocation.Parent;

                if (invocation.Equals(forEachStatement.Expression))
                {
                    return(true);
                }
            }

            return(false);
        }
        private void AnalyzeInvocationExpression(DocumentData documentData, InvocationExpressionSyntax node, InvokeFunctionReferenceData functionReferenceData)
        {
            var functionData     = functionReferenceData.FunctionData;
            var methodSymbol     = functionReferenceData.ReferenceSymbol;
            var functionNode     = functionData.GetNode();
            var functionBodyNode = functionData.GetBodyNode();
            var queryExpression  = node.Ancestors()
                                   .TakeWhile(o => o != functionNode)
                                   .OfType <QueryExpressionSyntax>()
                                   .FirstOrDefault();

            if (queryExpression != null)             // Await is not supported in a linq query
            {
                functionReferenceData.Ignore = true;
                Logger.Warn($"Cannot await async method in a query expression:\r\n{queryExpression}\r\n");
                return;
            }

            var searchOptions = AsyncCounterpartsSearchOptions.Default;

            if (_configuration.UseCancellationTokenOverload)
            {
                searchOptions |= AsyncCounterpartsSearchOptions.HasCancellationToken;
            }
            functionReferenceData.ReferenceAsyncSymbols = new HashSet <IMethodSymbol>(GetAsyncCounterparts(methodSymbol.OriginalDefinition, searchOptions));
            if (functionReferenceData.ReferenceAsyncSymbols.Any())
            {
                if (functionReferenceData.ReferenceAsyncSymbols.All(o => o.ReturnsVoid || !o.ReturnType.IsTaskType()))
                {
                    functionReferenceData.AwaitInvocation = false;
                    Logger.Info($"Cannot await method that is either void or do not return a Task:\r\n{methodSymbol}\r\n");
                }
                var nameGroups = functionReferenceData.ReferenceAsyncSymbols.GroupBy(o => o.Name).ToList();
                if (nameGroups.Count == 1)
                {
                    functionReferenceData.AsyncCounterpartName = nameGroups[0].Key;
                }
            }
            else if (!ProjectData.Contains(functionReferenceData.ReferenceSymbol))
            {
                // If we are dealing with an external method and there are no async counterparts for it, we cannot convert it to async
                functionReferenceData.Ignore = true;
                Logger.Info($"Method {methodSymbol} can not be async as there is no async counterparts for it");
                return;
            }
            else if (functionReferenceData.ReferenceFunctionData != null)
            {
                functionReferenceData.AsyncCounterpartName = functionReferenceData.ReferenceSymbol.Name + "Async";
            }

            // 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;
                    Logger.Info(
                        $"Cannot await invocation of a method that returns a Task without be synchronously awaited:\r\n{methodSymbol}\r\n");
                }
                else
                {
                    functionReferenceData.SynchronouslyAwaited = true;
                }
            }

            if (node.Parent.IsKind(SyntaxKind.ReturnStatement))
            {
                functionReferenceData.UseAsReturnValue = true;
            }

            // Calculate if node is the last statement
            if (node.Parent.Equals(functionBodyNode) ||       //eg. bool ExpressionReturn() => SimpleFile.Write();
                node.Equals(functionBodyNode)                 // eg. Func<bool> fn = () => SimpleFile.Write();
                )
            {
                functionReferenceData.LastInvocation   = true;
                functionReferenceData.UseAsReturnValue = !methodSymbol.ReturnsVoid;
            }
            var bodyBlock = functionBodyNode as BlockSyntax;

            if (bodyBlock?.Statements.Last() == node.Parent)
            {
                functionReferenceData.LastInvocation = true;
            }

            // Set CancellationTokenRequired if we detect that one of the async counterparts has a cancellation token as a parameter
            if (_configuration.UseCancellationTokenOverload &&
                functionReferenceData.ReferenceAsyncSymbols.Any(o => o.Parameters.Length > methodSymbol.Parameters.Length))
            {
                functionReferenceData.CancellationTokenRequired = true;
            }

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

            // Propagate CancellationTokenRequired to the method data only if the invocation can be async
            if (functionReferenceData.CancellationTokenRequired && functionReferenceData.GetConversion() == ReferenceConversion.ToAsync)
            {
                // We need to set CancellationTokenRequired to true for the method that contains this invocation
                var methodData = functionReferenceData.FunctionData.GetMethodData();
                methodData.CancellationTokenRequired = true;
            }
        }
        private T TransformFunctionReference <T>(T node, IFunctionAnalyzationResult funcResult, FunctionReferenceTransformationResult transfromReference,
                                                 ITypeTransformationMetadata typeMetadata,
                                                 INamespaceTransformationMetadata namespaceMetadata)
            where T : SyntaxNode
        {
            var nameNode                = node.GetAnnotatedNodes(transfromReference.Annotation).OfType <SimpleNameSyntax>().First();
            var funReferenceResult      = transfromReference.AnalyzationResult;
            var bodyFuncReferenceResult = funReferenceResult as IBodyFunctionReferenceAnalyzationResult;
            var newNameNode             = nameNode
                                          .WithIdentifier(Identifier(funReferenceResult.AsyncCounterpartName))
                                          .WithTriviaFrom(nameNode);

            transfromReference.Transformed = newNameNode;

            var cancellationTokenParamName = funcResult.GetMethodOrAccessor().CancellationTokenRequired ? "cancellationToken" : null;             // TODO: remove

            // If we have a cref change the name to the async counterpart and add/update arguments
            if (bodyFuncReferenceResult == null)
            {
                if (funReferenceResult.IsCref)
                {
                    var crefNode  = (NameMemberCrefSyntax)nameNode.Parent;
                    var paramList = new List <CrefParameterSyntax>();
                    // If the cref has already the parameters set then use them
                    if (crefNode.Parameters != null)
                    {
                        paramList.AddRange(crefNode.Parameters.Parameters);
                        // If the external async counterpart has a cancellation token, add it
                        if (funReferenceResult.AsyncCounterpartFunction == null &&
                            funReferenceResult.ReferenceSymbol.Parameters.Length <
                            funReferenceResult.AsyncCounterpartSymbol.Parameters.Length)
                        {
                            paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken))));
                        }
                    }
                    else
                    {
                        // We have to add the parameters to avoid ambiguity
                        var asyncSymbol = funReferenceResult.AsyncCounterpartSymbol;
                        paramList.AddRange(asyncSymbol.Parameters
                                           .Select(o => CrefParameter(o.Type
                                                                      .CreateTypeSyntax(true, namespaceMetadata.AnalyzationResult.IsIncluded(o.Type.ContainingNamespace?.ToString())))));
                    }

                    // If the async counterpart is internal and a token is required add a token parameter
                    if (funReferenceResult.AsyncCounterpartFunction?.GetMethodOrAccessor()?.CancellationTokenRequired == true)
                    {
                        paramList.Add(CrefParameter(IdentifierName(nameof(CancellationToken))));
                    }

                    node = node.ReplaceNestedNodes(
                        crefNode.Parent as QualifiedCrefSyntax,
                        crefNode,
                        crefNode
                        .ReplaceNode(nameNode, newNameNode)
                        .WithParameters(CrefParameterList(SeparatedList(paramList))),
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                          (type, fullName) => rootNode.WithContainer(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(rootNode.Container))),
                        childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata)
                        );
                }
                else if (funReferenceResult.IsNameOf)
                {
                    node = node.ReplaceNestedNodes(
                        nameNode.Parent as MemberAccessExpressionSyntax,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                          (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))),
                        childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata)
                        );
                }
                return(node);
            }
            // If we have a method passed as an argument we need to check if we have to wrap it inside a function
            if (bodyFuncReferenceResult.AsyncDelegateArgument != null)
            {
                if (bodyFuncReferenceResult.WrapInsideFunction)
                {
                    // TODO: move to analyze step
                    var  argumentNode  = nameNode.Ancestors().OfType <ArgumentSyntax>().First();
                    var  delReturnType = (INamedTypeSymbol)bodyFuncReferenceResult.AsyncDelegateArgument.ReturnType;
                    var  returnType    = bodyFuncReferenceResult.AsyncCounterpartSymbol.ReturnType;
                    bool returnTypeMismatch;
                    if (bodyFuncReferenceResult.ReferenceFunction != null)
                    {
                        var refMethod = bodyFuncReferenceResult.ReferenceFunction as IMethodAnalyzationResult;
                        if (refMethod != null && refMethod.PreserveReturnType)
                        {
                            returnTypeMismatch = !delReturnType.Equals(returnType);   // TODO Generics
                        }
                        else if (delReturnType.IsGenericType)                         // Generic Task
                        {
                            returnTypeMismatch = delReturnType.TypeArguments.First().IsAwaitRequired(returnType);
                        }
                        else
                        {
                            returnTypeMismatch = delReturnType.IsAwaitRequired(returnType);
                        }
                    }
                    else
                    {
                        returnTypeMismatch = !delReturnType.Equals(returnType);                         // TODO Generics
                    }

                    var newArgumentExpression = argumentNode.Expression
                                                .ReplaceNestedNodes(
                        nameNode.Parent as MemberAccessExpressionSyntax,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                          (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))),
                        childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata)
                        )
                                                .WrapInsideFunction(bodyFuncReferenceResult.AsyncDelegateArgument, returnTypeMismatch,
                                                                    namespaceMetadata.TaskConflict,
                                                                    invocation => invocation.AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult));
                    node = node
                           .ReplaceNode(argumentNode.Expression, newArgumentExpression);
                }
                else
                {
                    node = node.ReplaceNestedNodes(
                        nameNode.Parent as MemberAccessExpressionSyntax,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                          (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName))),
                        childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata)
                        );
                }
                return(node);
            }

            InvocationExpressionSyntax invokeNode = null;
            var isAccessor = bodyFuncReferenceResult.ReferenceSymbol.IsAccessor();

            if (!isAccessor && funReferenceResult.ReferenceNode.IsKind(SyntaxKind.InvocationExpression))
            {
                invokeNode = nameNode.Ancestors().OfType <InvocationExpressionSyntax>().First();
            }

            if (!bodyFuncReferenceResult.AwaitInvocation)
            {
                // An arrow method does not have a statement
                var statement = nameNode.Ancestors().OfType <StatementSyntax>().FirstOrDefault();
                var statementInParentFunction = nameNode.Ancestors().TakeWhile(o => !o.Equals(statement)).Any(o => o.IsFunction());
                var newNode = (SyntaxNode)statement ?? node;

                if (invokeNode != null)
                {
                    newNode = newNode.ReplaceNestedNodes(
                        invokeNode,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode
                                                                          .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult),
                                                                          funcResult, funReferenceResult, namespaceMetadata,
                                                                          (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression)))
                        );
                }
                else if (isAccessor)
                {
                    newNode = ConvertAccessor(newNode, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult,
                                              invNode => UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                                               (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression))));
                }
                else
                {
                    newNode = newNode.ReplaceNestedNodes(
                        nameNode.Parent as MemberAccessExpressionSyntax,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                          (type, fullName) => rootNode.WithExpression(type.CreateTypeSyntax(false, fullName).WithTriviaFrom(rootNode.Expression))),
                        childNode => RunReferenceTransformers(childNode, funcResult, funReferenceResult, namespaceMetadata)
                        );
                }

                if (statement != null && !statement.IsKind(SyntaxKind.LocalFunctionStatement))
                {
                    // Skip adding return statement for arrow functions
                    if (bodyFuncReferenceResult.UseAsReturnValue && !statementInParentFunction)
                    {
                        newNode = ((StatementSyntax)newNode).ToReturnStatement();
                    }
                    node = node
                           .ReplaceNode(statement, newNode);
                }
                else
                {
                    node = (T)newNode;
                }
            }
            else
            {
                // We need to annotate the invocation node because of the AddAwait method as it needs the parent node
                var invokeAnnotation = Guid.NewGuid().ToString();
                if (isAccessor)
                {
                    node = ConvertAccessor(node, nameNode, newNameNode, cancellationTokenParamName, bodyFuncReferenceResult, invNode =>
                                           UpdateTypeAndRunReferenceTransformers(invNode, funcResult, funReferenceResult, namespaceMetadata,
                                                                                 (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression)))
                                           .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation))
                                           );
                }
                else
                {
                    node = node.ReplaceNestedNodes(
                        invokeNode,
                        nameNode,
                        newNameNode,
                        rootNode => UpdateTypeAndRunReferenceTransformers(rootNode
                                                                          .AddCancellationTokenArgumentIf(cancellationTokenParamName, bodyFuncReferenceResult),
                                                                          funcResult, funReferenceResult, namespaceMetadata,
                                                                          (memberNode, type, fullName) => memberNode.WithExpression(type.CreateTypeSyntax(true, fullName).WithTriviaFrom(memberNode.Expression)))
                        .WithAdditionalAnnotations(new SyntaxAnnotation(invokeAnnotation))
                        );
                }

                invokeNode = node.GetAnnotatedNodes(invokeAnnotation).OfType <InvocationExpressionSyntax>().First();

                // Check if the invocation has a ?.
                var conditionalAccessNode = invokeNode.Ancestors()
                                            .TakeWhile(o => !(o is StatementSyntax))
                                            .OfType <ConditionalAccessExpressionSyntax>()
                                            .FirstOrDefault(o => o.WhenNotNull.Contains(invokeNode));
                if (conditionalAccessNode != null)                 // ?. syntax
                {
                    // We have to find out which strategy to use, if we have a non assignable expression, we are force to use if statements
                    // otherwise a ternary condition will be used
                    if (!conditionalAccessNode.Parent.IsKind(SyntaxKind.ExpressionStatement) || !invokeNode.Equals(conditionalAccessNode.WhenNotNull))
                    {
                        node = TransformConditionalAccessToConditionalExpressions(node, nameNode, funReferenceResult, typeMetadata,
                                                                                  conditionalAccessNode, invokeNode);
                    }
                    else
                    {
                        node = TransformConditionalAccessToIfStatements(node, nameNode, typeMetadata, conditionalAccessNode, invokeNode);
                    }
                }
                else
                {
                    node = node.ReplaceNode(invokeNode, invokeNode.AddAwait(_configuration.ConfigureAwaitArgument));
                }
            }
            return(node);
        }