File: SourceGeneration\TestGenerators.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
 
namespace Roslyn.Test.Utilities.TestGenerators
{
    internal class SingleFileTestGenerator : ISourceGenerator
    {
        private readonly List<(string content, string hintName)> _sources = new();
 
        public SingleFileTestGenerator()
        {
        }
 
        public SingleFileTestGenerator(string content, string? hintName = null)
        {
            AddSource(content, hintName);
        }
 
        public void AddSource(string content, string? hintName = null)
        {
            hintName ??= "generatedFile" + (_sources.Any() ? (_sources.Count + 1).ToString() : "");
            _sources.Add((content, hintName));
        }
 
        public void Execute(GeneratorExecutionContext context)
        {
            foreach (var (content, hintName) in _sources)
                context.AddSource(hintName, SourceText.From(content, Encoding.UTF8));
        }
 
        public void Initialize(GeneratorInitializationContext context)
        {
        }
    }
 
    /// <summary>
    /// A generator that produces diagnostics against existng source trees, rather than generating new content.
    /// </summary>
    internal class DiagnosticProducingGenerator : ISourceGenerator
    {
        public static readonly DiagnosticDescriptor Descriptor =
            new DiagnosticDescriptor(nameof(DiagnosticProducingGenerator), "Diagnostic Title", "Diagnostic Format", "Test", DiagnosticSeverity.Error, isEnabledByDefault: true);
 
        private readonly Func<GeneratorExecutionContext, Location> _produceLocation;
 
        public DiagnosticProducingGenerator(Func<GeneratorExecutionContext, Location> produceLocation)
        {
            _produceLocation = produceLocation;
        }
 
        public void Initialize(GeneratorInitializationContext context)
        {
        }
 
        public void Execute(GeneratorExecutionContext context)
        {
            context.ReportDiagnostic(Diagnostic.Create(Descriptor, _produceLocation(context)));
        }
    }
 
    internal class SingleFileTestGenerator2 : SingleFileTestGenerator
    {
        public SingleFileTestGenerator2(string content, string hintName = "generatedFile") : base(content, hintName)
        {
        }
    }
 
#pragma warning disable RS0062 // Do not implicitly capture primary constructor paramters
    internal class CallbackGenerator(
        Action<GeneratorInitializationContext> onInit,
        Action<GeneratorExecutionContext> onExecute,
        Func<ImmutableArray<(string hintName, SourceText? sourceText)>> computeSourceTexts)
        : ISourceGenerator
    {
        public CallbackGenerator(Action<GeneratorInitializationContext> onInit, Action<GeneratorExecutionContext> onExecute, string? source = "")
            : this(onInit, onExecute, () => ("source", source))
        {
        }
 
        public CallbackGenerator(Action<GeneratorInitializationContext> onInit, Action<GeneratorExecutionContext> onExecute, Func<(string hintName, string? source)> computeSource)
            : this(onInit, onExecute, () =>
            {
                var (hint, source) = computeSource();
                return ImmutableArray.Create((hint, string.IsNullOrWhiteSpace(source)
                    ? null
                    : SourceText.From(source, Encoding.UTF8)));
            })
        {
        }
 
        public CallbackGenerator(Func<(string hintName, string? source)> computeSource)
            : this(onInit: static _ => { }, onExecute: static _ => { }, () =>
            {
                var (hint, source) = computeSource();
                return ImmutableArray.Create((hint, string.IsNullOrWhiteSpace(source)
                    ? null
                    : SourceText.From(source, Encoding.UTF8)));
            })
        {
        }
 
        public void Initialize(GeneratorInitializationContext context)
            => onInit(context);
 
        public void Execute(GeneratorExecutionContext context)
        {
            onExecute(context);
 
            foreach (var (hintName, sourceText) in computeSourceTexts())
            {
                if (sourceText != null)
                    context.AddSource(hintName, sourceText);
            }
        }
    }
#pragma warning restore RS0062 // Do not implicitly capture primary constructor paramters
 
    internal class CallbackGenerator2 : CallbackGenerator
    {
        public CallbackGenerator2(Action<GeneratorInitializationContext> onInit, Action<GeneratorExecutionContext> onExecute, string? source = "") : base(onInit, onExecute, source)
        {
        }
    }
 
    [DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
    internal class InMemoryAdditionalText : AdditionalText
    {
        private readonly SourceText _content;
 
        public InMemoryAdditionalText(string path, string content)
        {
            Path = path;
            _content = SourceText.From(content, Encoding.UTF8);
        }
 
        public override string Path { get; }
 
        public override SourceText GetText(CancellationToken cancellationToken = default) => _content;
 
        internal class BinaryText : InMemoryAdditionalText
        {
            public BinaryText(string path) : base(path, string.Empty) { }
 
            public override SourceText GetText(CancellationToken cancellationToken = default) => throw new InvalidDataException("Binary content not supported");
        }
 
        internal string GetDebuggerDisplay()
        {
            return $"'{Path}': '{_content.Lines[0]}'";
        }
    }
 
    internal sealed class PipelineCallbackGenerator : IIncrementalGenerator
    {
        private readonly Action<IncrementalGeneratorInitializationContext> _registerPipelineCallback;
 
        public PipelineCallbackGenerator(Action<IncrementalGeneratorInitializationContext> registerPipelineCallback)
        {
            _registerPipelineCallback = registerPipelineCallback;
        }
 
        public void Initialize(IncrementalGeneratorInitializationContext context) => _registerPipelineCallback(context);
    }
 
    internal sealed class PipelineCallbackGenerator2 : IIncrementalGenerator
    {
        private readonly Action<IncrementalGeneratorInitializationContext> _registerPipelineCallback;
 
        public PipelineCallbackGenerator2(Action<IncrementalGeneratorInitializationContext> registerPipelineCallback)
        {
            _registerPipelineCallback = registerPipelineCallback;
        }
 
        public void Initialize(IncrementalGeneratorInitializationContext context) => _registerPipelineCallback(context);
    }
 
    internal sealed class IncrementalAndSourceCallbackGenerator : CallbackGenerator, IIncrementalGenerator
    {
        private readonly Action<IncrementalGeneratorInitializationContext> _onInit;
 
        public IncrementalAndSourceCallbackGenerator(Action<GeneratorInitializationContext> onInit, Action<GeneratorExecutionContext> onExecute, Action<IncrementalGeneratorInitializationContext> onIncrementalInit)
            : base(onInit, onExecute)
        {
            _onInit = onIncrementalInit;
        }
 
        public void Initialize(IncrementalGeneratorInitializationContext context) => _onInit(context);
    }
}