dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.06k stars 4.04k forks source link

Question: how could I compile code with source generator #67296

Closed WeihanLi closed 1 year ago

WeihanLi commented 1 year ago

For example, I want to compile the code like below

Console.WriteLine($"Hello is {RegexHelper.IsLowercase("Hello")}");

public partial class RegexHelper
{
    // The Source Generator generates the code of the method at compile time
    [System.Text.RegularExpressions.GeneratedRegex("^[a-z]+$")]
    public static partial Regex LowercaseLettersRegex();

    public static bool IsLowercase(string value)
    {
        return LowercaseLettersRegex().IsMatch(value);
    }
}

When I try to compile the code, I got an error as follows(I had referenced the analyzer dlls, may in a wrong way):

CS8795-Error-(10,64): error CS8795: Partial method 'RegexHelper.LowercaseLettersRegex()' must have an implementation part because it has accessibility modifiers.

The code I used to compile the code: https://github.com/WeihanLi/SamplesInPractice/blob/ac8507c68390a6e7cc3daf1a5df35ee6b852ef86/RoslynSample/SourceGeneratorSample.cs

Are there some examples on how to compile with the AnalyzerReference or SourceGenerator?

thomasclaudiushuber commented 1 year ago

Works with .NET 7.0, fails with .NET 6.0, as the System.Text.RegularExpressions.GeneratedRegex was introduced with .NET 7.0 only.

The error that comes with .NET 6.0 regarding the access modifier is because that attribute is missing in .NET 6.0.

image
WeihanLi commented 1 year ago

@thomasclaudiushuber yeah, the regex source generator is introduced since .NET 7, it works for .NET 7 and above. And my problem is I want to compile the code in runtime with Roslyn which failed to compile and I test with a .NET 8 console app

There's the code I used to compile the code

https://github.com/WeihanLi/SamplesInPractice/blob/ac8507c68390a6e7cc3daf1a5df35ee6b852ef86/RoslynSample/SourceGeneratorSample.cs

var code = @"
        using System;
        using System.Text.RegularExpressions;

        Console.WriteLine($""Hello is {RegexHelper.IsLowercase(""Hello"")}"");

        public partial class RegexHelper
        {
            [System.Text.RegularExpressions.GeneratedRegex(""^[a-z]+$"")]
            public static partial Regex LowercaseLettersRegex();

            public static bool IsLowercase(string value)
            {
                return LowercaseLettersRegex().IsMatch(value);
            }
        }";

var syntaxTree = CSharpSyntaxTree.ParseText(code);

var assemblyName = $"MyApp.{Guid.NewGuid()}";

// Add a reference to the System.Text.RegularExpressions assembly
var references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(System.Text.RegularExpressions.Regex).Assembly.Location),
};

// reference analyzer dll contains the source generator, harded for local testing purpose only
var generatorProjectPath = @"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\8.0.0-preview.3.23174.8\analyzers\dotnet\cs\System.Text.Json.SourceGeneration.dll";
var projectReference = MetadataReference.CreateFromFile(generatorProjectPath);

var compilation = CSharpCompilation.Create(
    assemblyName,
    syntaxTrees: new[] { syntaxTree },
    references: references.Append(projectReference),
    options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));

using var ms = new MemoryStream();
var result = compilation.Emit(ms);

if (result.Success)
{
    ms.Seek(0, System.IO.SeekOrigin.Begin);
    var assembly = Assembly.Load(ms.ToArray());
    assembly.EntryPoint?.Invoke(null, Array.Empty<object>());
}
else
{
    Console.WriteLine($"Compilation errors:");
    foreach (var diagnostic in result.Diagnostics)
    {
        Console.WriteLine(diagnostic.ToString());
    }
}
CyrusNajmabadi commented 1 year ago

There's the code I used to compile the code

I don't see you using generators anywhere there? You'll need to use GeneratorDriver to actually run generators.