/// <summary>
        /// Constructs the null-type flow graph and infers nullabilities for the nodes.
        /// </summary>
        public void Analyze(CancellationToken cancellationToken)
        {
            Parallel.ForEach(compilation.SyntaxTrees,
                             new ParallelOptions {
                CancellationToken = cancellationToken
            },
                             t => CreateNodes(t, cancellationToken));

            Parallel.ForEach(compilation.SyntaxTrees,
                             new ParallelOptions {
                CancellationToken = cancellationToken
            },
                             t => CreateEdges(t, cancellationToken));

            MaximumFlow.Compute(typeSystem.AllNodes, typeSystem.NullableNode, typeSystem.NonNullNode, cancellationToken);

            // Run non-null with ignoreEdgesWithoutCapacity before nullable so that errors
            // are reported as close to non-null as possible.
            typeSystem.NonNullNode.NullType = NullType.Infer;
            InferNonNull(typeSystem.NonNullNode, ignoreEdgesWithoutCapacity: true);
            typeSystem.NullableNode.NullType = NullType.Infer;
            InferNullable(typeSystem.NullableNode, ignoreEdgesWithoutCapacity: false);

            // There's going to be a bunch of remaining nodes where either choice would work.
            // For parameters, prefer marking those as nullable:
            foreach (var paramNode in typeSystem.NodesInInputPositions)
            {
                InferNullable(paramNode.ReplacedWith);
            }
            foreach (var node in typeSystem.AllNodes)
            {
                // Finally, anything left over is inferred to be non-null:
                if (node.NullType == NullType.Infer)
                {
                    if (node.ReplacedWith.NullType != NullType.Infer)
                    {
                        node.NullType = node.ReplacedWith.NullType;
                    }
                    else
                    {
                        node.NullType = NullType.NonNull;
                    }
                }
                Debug.Assert(node.NullType == node.ReplacedWith.NullType);
            }
        }
        /// <summary>
        /// Constructs the null-type flow graph and infers nullabilities for the nodes.
        /// </summary>
        public void Analyze(ConflictResolutionStrategy strategy, CancellationToken cancellationToken)
        {
            Parallel.ForEach(compilation.SyntaxTrees,
                             new ParallelOptions {
                CancellationToken = cancellationToken
            },
                             t => CreateNodes(t, cancellationToken));

            Parallel.ForEach(compilation.SyntaxTrees,
                             new ParallelOptions {
                CancellationToken = cancellationToken
            },
                             t => CreateEdges(t, cancellationToken));

            switch (strategy)
            {
            case ConflictResolutionStrategy.MinimizeWarnings:
                MaximumFlow.Compute(typeSystem.AllNodes, typeSystem.NullableNode, typeSystem.NonNullNode, cancellationToken);

                // Infer non-null first using the residual graph.
                InferNonNullUsingResidualGraph(typeSystem.NonNullNode);
                // Then use the original graph to infer nullable types everywhere we didn't already infer non-null.
                // This ends up creating the minimum cut.
                InferNullable(typeSystem.NullableNode);
                // Note that for longer chains (null -> A -> B -> C -> nonnull)
                // this approach ends up cutting the graph as close to nonnull as possible when there's multiple
                // choices with the same number of warnings. This is why we use the "reverse" residual graph
                // (ResidualGraphPredecessors) -- using ResidualGraphSuccessors would end up cutting closer to the <null> node.
                break;

            case ConflictResolutionStrategy.PreferNull:
                InferNullable(typeSystem.NullableNode);
                InferNonNull(typeSystem.NonNullNode);
                break;

            case ConflictResolutionStrategy.PreferNotNull:
                InferNonNull(typeSystem.NonNullNode);
                InferNullable(typeSystem.NullableNode);
                break;

            default:
                throw new NotSupportedException(strategy.ToString());
            }

            // There's going to be a bunch of remaining nodes where either choice would work.
            // For parameters, prefer marking those as nullable:
            foreach (var paramNode in typeSystem.NodesInInputPositions)
            {
                if (paramNode.ReplacedWith.NullType == NullType.Infer)
                {
                    InferNullable(paramNode.ReplacedWith);
                }
            }
            foreach (var node in typeSystem.AllNodes)
            {
                // Finally, anything left over is inferred to be non-null:
                if (node.NullType == NullType.Infer)
                {
                    if (node.ReplacedWith.NullType != NullType.Infer)
                    {
                        node.NullType = node.ReplacedWith.NullType;
                    }
                    else
                    {
                        node.NullType = NullType.NonNull;
                    }
                }
                Debug.Assert(node.NullType == node.ReplacedWith.NullType);
            }
        }