File: SourceGeneration\Nodes\GeneratorRunStateTable.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    internal sealed class GeneratorRunStateTable
    {
        private GeneratorRunStateTable(ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> executedSteps, ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> outputSteps)
        {
            ExecutedSteps = executedSteps;
            OutputSteps = outputSteps;
        }
 
        public ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> ExecutedSteps { get; }
 
        public ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> OutputSteps { get; }
 
        public sealed class Builder
        {
            private readonly Dictionary<string, HashSet<IncrementalGeneratorRunStep>>? _namedSteps;
            private readonly Dictionary<string, HashSet<IncrementalGeneratorRunStep>>? _outputSteps;
 
            public Builder(bool recordingExecutedSteps)
            {
                if (recordingExecutedSteps)
                {
                    _namedSteps = new Dictionary<string, HashSet<IncrementalGeneratorRunStep>>();
                    _outputSteps = new Dictionary<string, HashSet<IncrementalGeneratorRunStep>>();
                }
            }
 
            public void RecordStepsFromOutputNodeUpdate(IStateTable table)
            {
                Debug.Assert(RecordingExecutedSteps);
                Debug.Assert(table.HasTrackedSteps);
                foreach (var step in table.Steps)
                {
                    RecordStepTree(step, addToOutputSteps: true);
                }
            }
 
            [MemberNotNullWhen(true, nameof(_namedSteps), nameof(_outputSteps))]
            public bool RecordingExecutedSteps => _namedSteps is not null;
 
            public GeneratorRunStateTable ToImmutableAndFree()
            {
                return new GeneratorRunStateTable(StepCollectionToImmutable(_namedSteps), StepCollectionToImmutable(_outputSteps));
            }
 
            private static ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>> StepCollectionToImmutable(Dictionary<string, HashSet<IncrementalGeneratorRunStep>>? builder)
            {
                if (builder is null)
                {
                    return ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Empty;
                }
 
                ImmutableDictionary<string, ImmutableArray<IncrementalGeneratorRunStep>>.Builder resultBuilder = ImmutableDictionary.CreateBuilder<string, ImmutableArray<IncrementalGeneratorRunStep>>();
 
                foreach (var stepsByName in builder)
                {
                    resultBuilder.Add(stepsByName.Key, stepsByName.Value.ToImmutableArrayOrEmpty());
                }
 
                return resultBuilder.ToImmutable();
            }
 
            private void RecordStepTree(IncrementalGeneratorRunStep step, bool addToOutputSteps)
            {
                Debug.Assert(RecordingExecutedSteps);
                foreach (var (inputStep, _) in step.Inputs)
                {
                    RecordStepTree(inputStep, addToOutputSteps: false);
                }
 
                // If the step name is not null, we record it in the easily accessible collections.
                // Otherwise, it will be available only through graph traversal.
                if (step.Name is not null)
                {
                    addToNamedStepCollection(_namedSteps, step);
                    if (addToOutputSteps)
                    {
                        addToNamedStepCollection(_outputSteps, step);
                    }
                }
 
                static void addToNamedStepCollection(Dictionary<string, HashSet<IncrementalGeneratorRunStep>> stepCollectionBuilder, IncrementalGeneratorRunStep step)
                {
                    Debug.Assert(step.Name is not null);
                    if (!stepCollectionBuilder.TryGetValue(step.Name, out var stepsByName))
                    {
                        stepsByName = new HashSet<IncrementalGeneratorRunStep>();
                        stepCollectionBuilder.Add(step.Name, stepsByName);
                    }
                    stepsByName.Add(step);
                }
            }
        }
    }
}