Search code examples
c#.netroslynroslyn-code-analysissourcegenerators

I created a c# Roslyn source generator and a test, the generator works but the test doesn't find the same source


I created a simple source generator that works fine

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;

namespace TestGenerator;

[Generator]
public class TestGenerator : IIncrementalGenerator
{

    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // Register a syntax receiver to collect information during the initial parsing phase
        var classDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName(
                "TestGenerator.TestSourceGeneratorAttribute",

                predicate: static (s, _) => (s is ClassDeclarationSyntax || s is StructDeclarationSyntax) && s is TypeDeclarationSyntax type && type.Modifiers.Any(SyntaxKind.PartialKeyword),
                transform: static (ctx, _) =>
                {
                    if (ctx.TargetNode is TypeDeclarationSyntax typeDeclarationSyntax)
                    {
                        return typeDeclarationSyntax.Identifier.ToString();
                    }
                    return null;
                })
            .Where(static m => m is not null);
        var compilationAndClasses = classDeclarations.Collect();

        // Set up the generation phase
        context.RegisterSourceOutput(compilationAndClasses, static (spc, classes) =>
        {
            foreach (var typeDefinition in classes)
            {
                // Add the generated source to the output
                spc.AddSource($"{typeDefinition}.g.cs",
                    SourceText.From(GetSource(typeDefinition), Encoding.UTF8));
            }
        });
    }

    private static string GetSource(string source)
    {
        return $"""
            //<auto-generated/>
            #nullable enable
            using System;
            
            /// {source}
            """;
    }
}

Added a class to a test project

namespace TestGenerator.Test
{
    [TestSourceGenerator]
    public partial class TestClassGenerated
    {

    }
}

and the source generated fine

//<auto-generated/>
#nullable enable
using System;

/// TestClassGenerated

then I created a unit test

using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;

namespace TestGenerator.Test;

[TestClass]
public class TestGeneratorUnitTest
{
    [TestMethod]
    public async Task Test123()
    {
        // Arrange
        var source = """
            using System;

            namespace TestGenerator.Test
            {
                [TestSourceGenerator]
                public partial class MyClass
                {

                }
            }
            """;

        var expectedGeneratedCode = @"
                //<auto-generated/>
                #nullable enable
                using System;

                /// MyClass
                ";

        var test = new CSharpSourceGeneratorTest<TestGenerator, DefaultVerifier>
        {
            TestState =
            {
                Sources = { source },
                GeneratedSources =
                {
                    (typeof(TestGenerator), "MyClass.g.cs", expectedGeneratedCode)
                }
            }
        };

        // Act & Assert
        await test.RunAsync();
    }
}

and for the test it doesn't filter the class, the predicate is true but it doesn't enter in the transform

I created a repro to add here

https://github.com/luizfbicalho/TestSourceGenerator

I expect the test to generate the MyClass File


Solution

  • I got help and this is the solution

    I missed the references

          var references = AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
        .Select(a => MetadataReference.CreateFromFile(a.Location))
    
          CSharpCompilation compilation = CSharpCompilation.Create(
              assemblyName: "Tests",
              syntaxTrees: new[] { syntaxTree },
              references: references,
              options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable));