0

My generator needs to find all types in referenced projects which meet a specific criteria and generate a class for each of them using their method return types.

Example input (in a referenced project, not the one that references generator):

class SuitableClass
{
     public CustomType1 GetCustomType1() { ... }
     public CustomType2 GetCustomType2() { ... }
}

class SuitableClass2
{
     public CustomType3 GetCustomType4() { ... }
     public CustomType4 GetCustomType4() { ... }
}

Example output:

public class SuitableClassGenerated
{
    public void Handle(CustomType1) { ... }
    public void Handle(CustomType2) { ... }
}

public class SuitableClass2Generated
{
    public void Handle(CustomType3) { ... }
    public void Handle(CustomType4) { ... }
}

Generator code:

public void Initialize(IncrementalGeneratorInitializationContext context)
{
    IncrementalValuesProvider<string> typeNames = context
        .CompilationProvider
        .SelectMany((c, _) => GetAllSupportedTypes(c.SourceModule.ReferencedAssemblySymbols)
            .Select(x => x.ContainingNamespace.ToDisplayString() + "." + x.MetadataName));

    IncrementalValuesProvider<Info> generationInput = typeNames
        .Combine(context.CompilationProvider)
        .Select((c, _) =>
        {
            var clientType = c.Right.GetTypeByMetadataName(c.Left);
            if (clientType == null)
                return default;
            return new Info(clientType.Name,
                clientType.GetMembers().OfType<IMethodSymbol>()
                    .Where(m => m is { DeclaredAccessibility: Accessibility.Public, IsStatic: false, ReturnsVoid: false })
                    .Select(m => m.ReturnType.ToDisplayString())
                    .ToImmutableArray());
        })
        .Where(x => x.Methods != null);

    context.RegisterSourceOutput(generationInput,
        (ctx, input) =>
        {
            var code = GenerateCode(input);
            if (code != null)
                ctx.AddSource($"{input.Name}.g.cs", code);
        });
}

record struct Info(string Name, ImmutableArray<string> Methods);

As you see, the two stages require an access to Compilation which breaks the whole caching idea. Is there a way to make caching work so that some stages can be omitted? For an example, if I add a method with a new return type, I only need the second stage to be rerun.

Is there a way to rewrite such multi-stage Compilation-related pipeline to make it more cache-friendly?

2
  • Why do you access the CompilationProvider and not the SyntaxProvider? Any specific reason? Furthermore how do you determine the suitable class, via an attribute? Commented Apr 4, 2024 at 17:44
  • @MaikHasler, as far as I know, SyntaxProvider can only access items in the current assembly but I need to inspect the dependencies. There is no point in referencing the generator from inside those dependencies because the code I need to inspect is generated there by another generator so it will not be visible. I determine suitable class by a specific name and modifiers (public, static) and by method signatures inside (specific name pattern, modifiers, returning non void, absence of other members). I also need to inspect properties on the return types. Commented Apr 4, 2024 at 19:47

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.