Skip to content
This repository has been archived by the owner on Dec 12, 2020. It is now read-only.

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

Open
amura11 opened this issue Sep 30, 2020 · 2 comments
Labels

Comments

@amura11
Copy link

amura11 commented Sep 30, 2020

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 😄

@amura11 amura11 changed the title Is it possible to create a new class from an attribute that targets methods? Question: Is it possible to create a new class from an attribute that targets methods? Sep 30, 2020
@AArnott
Copy link
Owner

AArnott commented Sep 30, 2020

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
Copy link
Collaborator

amis92 commented Sep 30, 2020

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

  • You want to implement the IRichCodeGenerator interface.
  • Your ICodeGenerator.GenerateAsync can throw, it won't be invoked if IRichCodeGenerator is implemented.
  • Your IRichCodeGenerator.GenerateRichAsync implementation must return the RichGenerationResult which will be merged with other generator's output (if any).
  • Your generated result (RichGenerationResult.Members) will no longer be "wrapped" into triggering attribute's parent type+namespace, you will need to do that yourself.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants