diff --git a/DecoratorGenerator.UnitTests/CSharpSourceGeneratorVerifier.cs b/DecoratorGenerator.UnitTests/CSharpSourceGeneratorVerifier.cs index 27020e2..8afac95 100644 --- a/DecoratorGenerator.UnitTests/CSharpSourceGeneratorVerifier.cs +++ b/DecoratorGenerator.UnitTests/CSharpSourceGeneratorVerifier.cs @@ -6,7 +6,7 @@ namespace DecoratorGenerator.UnitTests; -public static class CSharpSourceGeneratorVerifier where TSourceGenerator : ISourceGenerator, new() +public static class CSharpSourceGeneratorVerifier where TSourceGenerator : IIncrementalGenerator, new() { public class Test : CSharpSourceGeneratorTest { diff --git a/DecoratorGenerator.UnitTests/DecoratorGenerator.UnitTests.csproj b/DecoratorGenerator.UnitTests/DecoratorGenerator.UnitTests.csproj index e097ae4..5ca0807 100644 --- a/DecoratorGenerator.UnitTests/DecoratorGenerator.UnitTests.csproj +++ b/DecoratorGenerator.UnitTests/DecoratorGenerator.UnitTests.csproj @@ -11,17 +11,17 @@ - - - - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/DecoratorGenerator/DecoratorGenerator.csproj b/DecoratorGenerator/DecoratorGenerator.csproj index 391a46f..d9eae25 100644 --- a/DecoratorGenerator/DecoratorGenerator.csproj +++ b/DecoratorGenerator/DecoratorGenerator.csproj @@ -54,13 +54,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/DecoratorGenerator/Main.cs b/DecoratorGenerator/Main.cs index e76bdb0..7f947c9 100644 --- a/DecoratorGenerator/Main.cs +++ b/DecoratorGenerator/Main.cs @@ -1,42 +1,61 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; namespace DecoratorGenerator { [Generator] - public class Main : ISourceGenerator + public class Main : IIncrementalGenerator { - public void Initialize(GeneratorInitializationContext context) { - // No initialization required for this one + public void Initialize(IncrementalGeneratorInitializationContext context) { + var types = context.SyntaxProvider.ForAttributeWithMetadataName( + $"DecoratorGenerator.{nameof(DecorateAttribute)}", + predicate: IsSyntaxTargetForGeneration, + transform: GetSemanticTargetForGeneration + ); + + var thirdPartyTypes = context.CompilationProvider.SelectMany(GetThirdPartySemanticTargetsForGeneration); + + context.RegisterSourceOutput(types, Execute); + context.RegisterSourceOutput(thirdPartyTypes, Execute); + } + + private static bool IsSyntaxTargetForGeneration(SyntaxNode syntaxNode, CancellationToken token) { + return syntaxNode is InterfaceDeclarationSyntax; } - public void Execute(GeneratorExecutionContext context) { - var types = GetAllDecoratedTypes(context.Compilation.Assembly.GlobalNamespace); + private static INamedTypeSymbol GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { + var intefaceSyntax = context.TargetNode as InterfaceDeclarationSyntax; + var symbol = context.SemanticModel.GetDeclaredSymbol(intefaceSyntax, cancellationToken: cancellationToken) as INamedTypeSymbol; + + return symbol; + } + + private static void Execute(SourceProductionContext context, INamedTypeSymbol typeSymbol) { + var (source, className) = OutputGenerator.GenerateOutputs(typeSymbol); + + context.AddSource($"{className}.generated.cs", SourceText.From(source, Encoding.UTF8, SourceHashAlgorithm.Sha256)); + } + + private IEnumerable GetThirdPartySemanticTargetsForGeneration(Compilation compilation, CancellationToken _) { + var wrapperSymbolWithoutNamespace = compilation.Assembly.GetTypeByMetadataName("WrapperList"); - var wrapperSymbolWithoutNamespace = context.Compilation.Assembly.GetTypeByMetadataName("WrapperList"); var wrapperListTypes = - (wrapperSymbolWithoutNamespace == null) - ? GetAllTypes(context.Compilation.Assembly.GlobalNamespace, x => x.Name == "WrapperList") - : new[] { wrapperSymbolWithoutNamespace }; - var thirdPartyTypes = + (wrapperSymbolWithoutNamespace == null) + ? GetAllTypes(compilation.Assembly.GlobalNamespace, x => x.Name == "WrapperList") + : new[] { wrapperSymbolWithoutNamespace }; + + return wrapperListTypes.SelectMany(x => x.GetMembers() .Where(m => m.Name != ".ctor") .Select(m => m as IFieldSymbol) .Select(f => f.Type) .Select(t => t as INamedTypeSymbol)); - - types = types.Concat(thirdPartyTypes); - - var outputs = types.Select(OutputGenerator.GenerateOutputs); - - foreach (var (source, className) in outputs) { - // Add the source code to the compilation - context.AddSource($"{className}.generated.cs", SourceText.From(source, Encoding.UTF8, SourceHashAlgorithm.Sha256)); - } } /// @@ -58,14 +77,5 @@ private IEnumerable GetAllTypes(INamespaceSymbol input, Func - /// Gets all Types in the namespace decorated with the including nested namespaces. - /// - /// - /// - private IEnumerable GetAllDecoratedTypes(INamespaceSymbol input) { - return GetAllTypes(input, (x) => x.GetAttributes().Any(att => att.AttributeClass.Name == nameof(DecorateAttribute))); - } } } \ No newline at end of file diff --git a/README.md b/README.md index 0e2cdde..c78b639 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ public interface ICat } ``` -Build the project so the abstract class is generated for the interface. The generated class will be named after the interface, but without the `I` prefix. In this case, since the interface is `ICat` the class will be `CatDecorator`. Then create your decorator class as usual: +Since this library is an [incremental source generator](https://github.com/dotnet/roslyn/blob/d8c21d64ca958840bdaa2898cb2324397dc57bbb/docs/features/incremental-generators.md), the abstract class should be generated after saving the changes to interface's file. The generated class will be named after the interface, but without the `I` prefix. In this case, since the interface is `ICat` the class will be `CatDecorator`. Then create your decorator class as usual: ```c# namespace SampleLibrary;