Search code examples
c#roslynsourcegeneratorscsharp-source-generator

Read parameters of attribute created by source generator


I have the following source code generator

[Generator]
public class Generator : ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        var output = @"using System;
namespace SourceGeneratorAttributes;
[AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
class GlobalAttribute : Attribute
{
    public int OptionalNumber { get; set; } = 10;
    public string OptionalString { get; set; } = ""test"";
    public GlobalAttribute(string requiredString, int requiredNumber) {}
}
";
        context.AddSource("GlobalAttribute.cs", SourceText.From(output, Encoding.UTF8));

        var attributes = context.Compilation.Assembly.GetAttributes();
        foreach (var attribute in attributes)
        {
            Console.WriteLine(attribute);
        }
    }
    public void Initialize(GeneratorInitializationContext context)
    {
    }
}

Then in the project where I use the source generator I have the following

[assembly:Global("text", 20, OptionalNumber = 33, OptionalString = "random")]

But if I break on Console.WriteLine(attribute); and inspect the attribute I get the following: enter image description here How do I find what the error is? and why are all arguments gone?

If I use a different attribute like AssemblyFileVersion then I can read the arguments as expected.

Full code can be found here: https://github.com/AnderssonPeter/SourceGeneratorAttributes


Solution

  • You have to create a separate class that has the Generator attribute that implements the IIncrementalGenerator and uses RegisterPostInitializationOutput to add the attribute

    Example:

    [Generator]
    public class AttributeGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            context.RegisterPostInitializationOutput(i =>
            {
                i.AddSource("GlobalParameterAttribute.g.cs", @"using System;
    namespace SourceGeneratorAttributes;
    [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
    public sealed class GlobalParameterAttribute : Attribute
    {
        public int OptionalNumber { get; set; } = 10;
        public string OptionalString { get; set; } = ""test"";
        public GlobalParameterAttribute(string requiredString, int requiredNumber) {}
    }");
            });
    
        }
    }
    

    Then you can use it in your normal generator like so

    [Generator]
    public class Generator : ISourceGenerator
    {
        public void Execute(GeneratorExecutionContext context)
        {
            var attributes = context.Compilation.Assembly.GetAttributes();
            foreach (var attribute in attributes)
            {
                Console.WriteLine(attribute);
            }
        }
        public void Initialize(GeneratorInitializationContext context)
        {
        }
    }