private DataFlowAnalysisResult <TAnalysisResult, TAbstractAnalysisValue> Run(ControlFlowGraph cfg)
        {
            var resultBuilder = new DataFlowAnalysisResultBuilder <TAnalysisData>();

            // Add each basic block to the result.
            foreach (var block in cfg.Blocks)
            {
                resultBuilder.Add(block);
            }

            var worklist = new Queue <BasicBlock>();
            var entry    = GetEntry(cfg);

            // Initialize the input of the initial block
            // with the default abstract value of the domain.
            UpdateInput(resultBuilder, entry, AnalysisDomain.Bottom);

            // Add the entry block to the worklist.
            worklist.Enqueue(entry);

            while (worklist.Count > 0)
            {
                // Get the next block to process
                // and its associated result.
                var block = worklist.Dequeue();

                // Get the outputs of all predecessor blocks of the current block.
                var inputs = GetPredecessors(block).Select(b => GetOutput(resultBuilder[b]));

                // Merge all the outputs to get the new input of the current block.
                var input = AnalysisDomain.Merge(inputs);

                // Merge might return one of the original input values if only one of them is a valid non-null value.
                if (inputs.Any(i => ReferenceEquals(input, i)))
                {
                    input = AnalysisDomain.Clone(input);
                }

                // Temporary workaround due to lack of *real* CFG
                // TODO: Remove the below if statement once we move to compiler's CFG
                // https://github.com/dotnet/roslyn-analyzers/issues/1567
                if (block.Kind == BasicBlockKind.Exit &&
                    OperationVisitor.MergedAnalysisDataAtReturnStatements != null)
                {
                    input = AnalysisDomain.Merge(input, OperationVisitor.MergedAnalysisDataAtReturnStatements);
                }

                // Compare the previous input with the new input.
                var compare = AnalysisDomain.Compare(GetInput(resultBuilder[block]), input);

                // The newly computed abstract values for each basic block
                // must be always greater or equal than the previous value
                // to ensure termination.
                Debug.Assert(compare <= 0, "The newly computed abstract value must be greater or equal than the previous one.");

                // Is old input value < new input value ?
                if (compare < 0 || block.Kind == BasicBlockKind.Entry)
                {
                    // The newly computed value is greater than the previous value,
                    // so we need to update the current block result's
                    // input values with the new ones.
                    UpdateInput(resultBuilder, block, AnalysisDomain.Clone(input));

                    // Flow the new input through the block to get a new output.
                    var output = Flow(block, input);

                    // Compare the previous output with the new output.
                    compare = AnalysisDomain.Compare(GetOutput(resultBuilder[block]), output);

                    // The newly computed abstract values for each basic block
                    // must be always greater or equal than the previous value
                    // to ensure termination.
                    Debug.Assert(compare <= 0, "The newly computed abstract value must be greater or equal than the previous one.");

                    // Is old output value < new output value ?
                    if (compare < 0 || block.Kind == BasicBlockKind.Entry)
                    {
                        // The newly computed value is greater than the previous value,
                        // so we need to update the current block result's
                        // output values with the new ones.
                        UpdateOutput(resultBuilder, block, output);

                        // Since the new output value is different than the previous one,
                        // we need to propagate it to all the successor blocks of the current block.
                        EnqueueRange(worklist, GetSuccessors(block));
                    }
                }
            }

            return(resultBuilder.ToResult(ToResult, OperationVisitor.GetStateMap(),
                                          OperationVisitor.GetPredicateValueKindMap(), OperationVisitor.GetMergedDataForUnhandledThrowOperations(),
                                          cfg, OperationVisitor.ValueDomain.UnknownOrMayBeValue));
        }