AArnott / CodeGeneration.Roslyn

Assists in performing Roslyn-based code generation during a build.
Microsoft Public License
408 stars 59 forks source link

Question: Is it possible to create a new class from an attribute that targets methods? #236

Open amura11 opened 4 years ago

amura11 commented 4 years ago

For simplicity assume my goal is to have an attribute on a method that generates a new class whose name is the methods names followed by "Wrapper".

For example if I have an attribute:

    [AttributeUsage(AttributeTargets.Method)]
    [CodeGenerationAttribute("FunctionGenerator.TestGenerator, FunctionGenerator")]
    [Conditional("CodeGeneration")]
    public class TestGeneratorAttribute : Attribute { }

And a class:

public class TestClass
{
    [TestGenerator()]
    public void FooBar() { }
}

I would want to generate the following class:

public class FooBarWrapper 
{ 
    /*Eventually useful code would go here*/ 
}

Of course I tried this with my crude understanding of code generation using this code in my generator:

public Task<SyntaxList<MemberDeclarationSyntax>> GenerateAsync(TransformationContext context, IProgress<Diagnostic> progress, CancellationToken cancellationToken)
{
    MethodDeclarationSyntax method = context.ProcessingNode as MethodDeclarationSyntax;

    var wrapper = SyntaxFactory.ClassDeclaration($"{method.Identifier}Wrapper");

    var results = SyntaxFactory.SingletonList<MemberDeclarationSyntax>(wrapper);
    return Task.FromResult(results);
}

Which generates:

/*Using statements and namespace removed for brevity*/
public class TestClass
{
    class FooBarWrapper
    {
    }
}

This is close to what I want but being nested under TestClass causes a build error because I now have 2 definitions of TestClass. I feel like there must be a way to accomplish my goal of creating a completely new (ie. not a class nested in another class).

Is this possible? If so, how?

I really appreciate any help on this 😄

AArnott commented 4 years ago

Yes, it's possible. We have a simple codegen endpoint which is apparently what you tried, which puts you in the same (partial) class. But we have a more "I own everything" endpoint that lets you generate the entire CompilationUnit (source file). The API has changed a lot though, so @amis92 is probably the best one to point you to the right API. I don't see it mentioned in the README.

amis92 commented 4 years ago

Hi, what you want to do is indeed doable as @AArnott guessed.

Suggestion

But first, let me suggest to you the new C# Source Generators feature shipping with .NET 5/C#9: https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/

The development experience currently is actually a bit worse compared to CG.R, but the future is there (we won't be pursuing CodeGeneration.Roslyn development any further).

Answer

Indeed there's no mention in README of the required feature - it's only described a little bit in https://github.com/AArnott/CodeGeneration.Roslyn/wiki/Features#irichcodegenerator