Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace DecoratorGenerator.UnitTests;

public static class CSharpSourceGeneratorVerifier<TSourceGenerator> where TSourceGenerator : ISourceGenerator, new()
public static class CSharpSourceGeneratorVerifier<TSourceGenerator> where TSourceGenerator : IIncrementalGenerator, new()
{
public class Test : CSharpSourceGeneratorTest<TSourceGenerator, DefaultVerifier>
{
Expand Down
16 changes: 8 additions & 8 deletions DecoratorGenerator.UnitTests/DecoratorGenerator.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@

<ItemGroup>
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.103.8" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.NUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
8 changes: 4 additions & 4 deletions DecoratorGenerator/DecoratorGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.100">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
66 changes: 38 additions & 28 deletions DecoratorGenerator/Main.cs
Original file line number Diff line number Diff line change
@@ -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<INamedTypeSymbol> 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));
}
}

/// <summary>
Expand All @@ -58,14 +77,5 @@ private IEnumerable<INamedTypeSymbol> GetAllTypes(INamespaceSymbol input, Func<I
}
}
}

/// <summary>
/// Gets all Types in the namespace decorated with the <see cref="DecorateAttribute"/> including nested namespaces.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private IEnumerable<INamedTypeSymbol> GetAllDecoratedTypes(INamespaceSymbol input) {
return GetAllTypes(input, (x) => x.GetAttributes().Any(att => att.AttributeClass.Name == nameof(DecorateAttribute)));
}
}
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading