private void SoftCopyAllDependencies(MethodOrAccessorData methodOrAccessor)
        {
            if (methodOrAccessor == null)
            {
                return;
            }
            var processedMethods = new HashSet <MethodOrAccessorData>();
            var processingMetods = new Queue <MethodOrAccessorData>();

            processingMetods.Enqueue(methodOrAccessor);

            while (processingMetods.Count > 0)
            {
                var currentOrAccessor = processingMetods.Dequeue();
                processedMethods.Add(currentOrAccessor);
                foreach (var referencedMethodOrAccessor in currentOrAccessor.BodyFunctionReferences
                         .Select(o => o.ReferenceFunctionData)
                         .Where(o => o != null)
                         .Where(o => o.Conversion != MethodConversion.Ignore && o.TypeData.IsNewType)
                         .Where(o => !processedMethods.Contains(o))
                         .OfType <MethodOrAccessorData>())
                {
                    referencedMethodOrAccessor.SoftCopy();
                    processingMetods.Enqueue(referencedMethodOrAccessor);
                }
            }
        }
        private List <IMethodSymbol> FillRelatedAsyncMethods(MethodOrAccessorData methodOrAccessorData, IMethodSymbol symbol)
        {
            var asyncConterparts = GetAsyncCounterparts(symbol, _preAnalyzeSearchOptions).ToList();

            if (asyncConterparts.Any())
            {
                foreach (var asyncConterpart in asyncConterparts)
                {
                    methodOrAccessorData.RelatedAsyncMethods.TryAdd(asyncConterpart);
                }
            }
            return(asyncConterparts);
        }
        private async Task ScanMethodData(MethodOrAccessorData methodOrAccessorData, int depth = 0, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested();
            }
            if (!_scannedMethodOrAccessors.TryAdd(methodOrAccessorData))
            {
                return;
            }

            SyntaxReference syntax;
            var             bodyScanMethodDatas  = new HashSet <MethodOrAccessorData>();
            var             referenceScanMethods = new HashSet <IMethodSymbol>();

            if (_configuration.ScanMethodBody || methodOrAccessorData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync))
            {
                bodyScanMethodDatas.Add(methodOrAccessorData);
            }

            var interfaceMethods = methodOrAccessorData.ImplementedInterfaces.ToImmutableHashSet();

            if (methodOrAccessorData.InterfaceMethod)
            {
                interfaceMethods = interfaceMethods.Add(methodOrAccessorData.Symbol);
            }
            // Get and save all interface implementations
            foreach (var interfaceMethod in interfaceMethods)
            {
                referenceScanMethods.Add(interfaceMethod);

                syntax = interfaceMethod.DeclaringSyntaxReferences.Single();
                var document = _solution.GetDocument(syntax.SyntaxTree);
                if (!CanProcessDocument(document))
                {
                    continue;
                }
                var documentData = ProjectData.GetDocumentData(document);
                var methodNode   = await syntax.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

                var interfaceMethodData = documentData.GetMethodOrAccessorData(methodNode);

                // NOTE: FindImplementationsAsync will not find all implementations when we have an abstract/virtual implementation of the interface.
                // In this case we will get only the abstract/virtual method so we have to find all overrides for it manually
                await FindImplementations(interfaceMethod, async (implMethod, implMethodData) =>
                {
                    interfaceMethodData.RelatedMethods.TryAdd(implMethodData);
                    implMethodData.RelatedMethods.TryAdd(interfaceMethodData);

                    if (_configuration.ScanMethodBody || implMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync))
                    {
                        bodyScanMethodDatas.Add(implMethodData);
                    }
                    if (!implMethod.IsAbstract && !implMethod.IsVirtual)
                    {
                        return;
                    }
                    // Find all overrides
                    await FindOverrides(implMethod, (overrideSymbol, overrideMethodData) =>
                    {
                        overrideMethodData.RelatedMethods.TryAdd(interfaceMethodData);
                        interfaceMethodData.RelatedMethods.TryAdd(overrideMethodData);
                        implMethodData.RelatedMethods.TryAdd(overrideMethodData);
                        overrideMethodData.RelatedMethods.TryAdd(implMethodData);
                        if (_configuration.ScanMethodBody || overrideMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync))
                        {
                            bodyScanMethodDatas.Add(overrideMethodData);
                        }
                    }, cancellationToken).ConfigureAwait(false);
                }, cancellationToken).ConfigureAwait(false);
            }

            MethodOrAccessorData baseMethodData   = null;
            IMethodSymbol        baseMethodSymbol = null;

            if (methodOrAccessorData.BaseOverriddenMethod?.DeclaringSyntaxReferences.Any() == true)
            {
                baseMethodSymbol = methodOrAccessorData.BaseOverriddenMethod;
            }
            else if (!methodOrAccessorData.InterfaceMethod && (methodOrAccessorData.Symbol.IsVirtual || methodOrAccessorData.Symbol.IsAbstract))             // interface method has IsAbstract true
            {
                baseMethodSymbol = methodOrAccessorData.Symbol;
                baseMethodData   = methodOrAccessorData;
            }

            // Get and save all derived methods
            if (baseMethodSymbol != null)
            {
                referenceScanMethods.Add(baseMethodSymbol);

                if (baseMethodData == null)
                {
                    syntax = baseMethodSymbol.DeclaringSyntaxReferences.Single();
                    var document = _solution.GetDocument(syntax.SyntaxTree);
                    if (CanProcessDocument(document))
                    {
                        var methodNode = await syntax.GetSyntaxAsync(cancellationToken).ConfigureAwait(false);

                        baseMethodData = ProjectData.GetDocumentData(document).GetMethodOrAccessorData(methodNode);
                    }
                }

                if (baseMethodData != null && (_configuration.ScanMethodBody || baseMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync)))
                {
                    bodyScanMethodDatas.Add(baseMethodData);
                }

                // Check if the overrides will be found in the FindImplementations callback, if so we must skip this call otherwise a race condition may happen
                if (baseMethodData?.ImplementedInterfaces.All(o => !interfaceMethods.Contains(o)) == true)
                {
                    await FindOverrides(baseMethodSymbol, (overrideMethod, overrideMethodData) =>
                    {
                        if (baseMethodData != null)
                        {
                            overrideMethodData.RelatedMethods.TryAdd(baseMethodData);
                            baseMethodData.RelatedMethods.TryAdd(overrideMethodData);
                        }
                        else
                        {
                            overrideMethodData.ExternalRelatedMethods.TryAdd(baseMethodSymbol);
                        }
                        if (!overrideMethod.IsAbstract && (_configuration.ScanMethodBody || overrideMethodData.Conversion.HasAnyFlag(MethodConversion.Smart, MethodConversion.ToAsync)))
                        {
                            bodyScanMethodDatas.Add(overrideMethodData);
                        }
                    }, cancellationToken).ConfigureAwait(false);
                }
            }

            if (baseMethodSymbol == null && !interfaceMethods.Any())             //TODO: what about hiding methods
            {
                referenceScanMethods.Add(methodOrAccessorData.Symbol);
            }

            foreach (var mData in bodyScanMethodDatas)
            {
                foreach (var method in FindNewlyInvokedMethodsWithAsyncCounterpart(mData, referenceScanMethods))
                {
                    await ScanAllMethodReferenceLocations(method, depth, cancellationToken).ConfigureAwait(false);
                }
            }
            foreach (var methodToScan in referenceScanMethods)
            {
                await ScanAllMethodReferenceLocations(methodToScan, depth, cancellationToken).ConfigureAwait(false);
            }
        }
        private void AnalyzeMethodData(DocumentData documentData, MethodOrAccessorData methodAccessorData)
        {
            if (methodAccessorData.Conversion == MethodConversion.Copy)
            {
                // If the method will be copied then we have to copy all methods that are used inside this one and do this recursively
                SoftCopyAllDependencies(methodAccessorData);
                return;                 // We do not want to analyze method that will be only copied
            }

            // If all abstract/virtual related methods are ignored then ignore also this one (IsAbstract includes also interface members)
            // Skip methods that were ignored because having an async counterpart in order to have overrides generated
            var baseMethods = methodAccessorData.RelatedMethods
                              .Where(o => !o.HasAsyncCounterpart || o.ExplicitlyIgnored)
                              .Where(o => o.Symbol.IsAbstract || o.Symbol.IsVirtual).ToList();

            if (!methodAccessorData.Conversion.HasFlag(MethodConversion.ToAsync) && baseMethods.Any() && baseMethods.All(o => o.Conversion == MethodConversion.Ignore))
            {
                if (methodAccessorData.TypeData.GetSelfAndAncestorsTypeData().Any(o => o.Conversion == TypeConversion.NewType))
                {
                    methodAccessorData.Copy();
                    // Check if there are any internal methods that are candidate to be async and are invoked inside this method
                    // If there are, then we need to copy them
                    SoftCopyAllDependencies(methodAccessorData);
                }
                else
                {
                    methodAccessorData.Ignore(IgnoreReason.AllRelatedMethodsIgnored);
                }
                return;
            }


            var methodBody = methodAccessorData.GetBodyNode();

            methodAccessorData.HasYields           = methodBody?.DescendantNodes().OfType <YieldStatementSyntax>().Any() == true;
            methodAccessorData.MustRunSynchronized = methodAccessorData.Symbol.GetAttributes()
                                                     .Where(o => o.AttributeClass.Name == "MethodImplAttribute")
                                                     .Any(o => ((MethodImplOptions)(int)o.ConstructorArguments.First().Value).HasFlag(MethodImplOptions.Synchronized));

            if (methodBody == null)
            {
                methodAccessorData.OmitAsync = true;
            }

            // Order by descending so we are sure that methods passed by argument will be processed before the invoked method with those arguments
            foreach (var reference in methodAccessorData.BodyFunctionReferences.OrderByDescending(o => o.ReferenceNameNode.SpanStart))
            {
                AnalyzeMethodReference(documentData, reference);
            }

            foreach (var reference in methodAccessorData.CrefFunctionReferences)
            {
                AnalyzeCrefMethodReference(documentData, methodAccessorData, reference);
            }

            foreach (var reference in methodAccessorData.NameofFunctionReferences)
            {
                AnalyzeNameofMethodReference(documentData, methodAccessorData, reference);
            }

            // Ignore all candidate arguments that are not an argument of an async invocation candidate
            // Do not ignore accessor as they are executed prior passing
            foreach (var reference in methodAccessorData.BodyFunctionReferences
                     .Where(o => !o.ReferenceSymbol.IsPropertyAccessor() && o.ReferenceNode.IsKind(SyntaxKind.Argument) && o.ArgumentOfFunctionInvocation == null))
            {
                reference.Ignore(IgnoreReason.InvokedMethodNoAsyncCounterpart);
            }

            if (methodAccessorData.Conversion.HasFlag(MethodConversion.ToAsync))
            {
                return;
            }

            // If a method is never invoked and there is no invocations inside the method body that can be async and there is no related methods we can ignore it.
            // Methods with Unknown may not have InvokedBy populated so we cannot ignore them here
            // Do not ignore methods that are inside a type with conversion NewType as ExternalRelatedMethods may not be populated
            // Do not ignore a method if any of its related methods has an async counterpart that was not ignored by user so that we can gnerate (e.g. async override)
            if (
                methodAccessorData.Dependencies.All(o => o.Conversion.HasFlag(MethodConversion.Ignore)) &&
                methodAccessorData.RelatedMethods.All(o => !o.HasAsyncCounterpart || o.ExplicitlyIgnored) &&
                methodAccessorData.BodyFunctionReferences.All(o => o.Conversion == ReferenceConversion.Ignore) &&
                methodAccessorData.Conversion.HasFlag(MethodConversion.Smart) &&
                methodAccessorData.TypeData.GetSelfAndAncestorsTypeData().All(o => o.Conversion != TypeConversion.NewType) &&
                !methodAccessorData.ExternalRelatedMethods.Any()
                )
            {
                methodAccessorData.Ignore(IgnoreReason.NeverUsedAndNoAsyncInvocations);
            }
        }
 private void AnalyzeNameofMethodReference(DocumentData documentData, MethodOrAccessorData methoData, NameofFunctionDataReference nameofData)
 {
     nameofData.RelatedBodyFunctionReferences.AddRange(
         methoData.BodyFunctionReferences.Where(o => o.ReferenceSymbol.Equals(nameofData.ReferenceSymbol)));
 }