From 45ef6eaf7af934e4752eca8507e978b1b2185e52 Mon Sep 17 00:00:00 2001 From: Colin Wilmans Date: Tue, 16 Apr 2024 11:47:10 +0200 Subject: [PATCH] Generate GenerateAutomaticInterfaceAttribute instead of asking that of the user. See cookbook: https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#generated-class --- AutomaticInterface/AutomaticInterface.sln | 126 ++++++++---------- .../AutomaticInterfaceGenerator.cs | 21 +++ .../AutomaticInterfaceAttribute.csproj | 8 -- .../GenerateAutomaticInterfaceAttribute.cs | 10 -- .../AutomaticInterfaceExample.csproj | 1 - .../AutomaticInterfaceExample/DemoClass.cs | 2 +- .../DemoClassWithCustomNamespace.cs | 2 +- .../InterfaceExample.cs | 2 +- .../RemoveThisOnNextVersionAttribute.cs | 14 ++ AutomaticInterface/TestNuget/Test.cs | 2 +- AutomaticInterface/TestNuget/TestNuget.csproj | 4 - AutomaticInterface/Tests/GeneratorTests.cs | 12 +- AutomaticInterface/Tests/Tests.csproj | 1 - README.md | 9 +- 14 files changed, 105 insertions(+), 109 deletions(-) delete mode 100644 AutomaticInterface/AutomaticInterfaceAttribute/AutomaticInterfaceAttribute.csproj delete mode 100644 AutomaticInterface/AutomaticInterfaceAttribute/GenerateAutomaticInterfaceAttribute.cs create mode 100644 AutomaticInterface/TestNuget/RemoveThisOnNextVersionAttribute.cs diff --git a/AutomaticInterface/AutomaticInterface.sln b/AutomaticInterface/AutomaticInterface.sln index f6d30ce..5fe5fd2 100644 --- a/AutomaticInterface/AutomaticInterface.sln +++ b/AutomaticInterface/AutomaticInterface.sln @@ -1,67 +1,59 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31702.278 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomaticInterfaceExample", "AutomaticInterfaceExample\AutomaticInterfaceExample.csproj", "{AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomaticInterface", "AutomaticInterface\AutomaticInterface.csproj", "{AA3F5AAE-44B7-4F33-8152-A5251B51AD79}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomaticInterfaceAttribute", "AutomaticInterfaceAttribute\AutomaticInterfaceAttribute.csproj", "{ADEA885F-9F6F-443E-945A-512223AEFC11}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{96FEC8F8-B0D8-41C9-B9C4-6D1B221D6128}" - ProjectSection(SolutionItems) = preProject - Directory.Build.targets = Directory.Build.targets - ..\.github\workflows\action.yml = ..\.github\workflows\action.yml - ..\README.md = ..\README.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestNuget", "TestNuget\TestNuget.csproj", "{E7CF6623-54D8-4A40-A1B1-CE078551EFE1}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - DebugGenerator|Any CPU = DebugGenerator|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Release|Any CPU.Build.0 = Release|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Release|Any CPU.Build.0 = Release|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ADEA885F-9F6F-443E-945A-512223AEFC11}.Release|Any CPU.Build.0 = Release|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Release|Any CPU.Build.0 = Release|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.DebugGenerator|Any CPU.ActiveCfg = Debug|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.DebugGenerator|Any CPU.Build.0 = Debug|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B144CF18-21D8-4CB6-9847-DDE7451543D4} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34728.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomaticInterfaceExample", "AutomaticInterfaceExample\AutomaticInterfaceExample.csproj", "{AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomaticInterface", "AutomaticInterface\AutomaticInterface.csproj", "{AA3F5AAE-44B7-4F33-8152-A5251B51AD79}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{96FEC8F8-B0D8-41C9-B9C4-6D1B221D6128}" + ProjectSection(SolutionItems) = preProject + ..\.github\workflows\action.yml = ..\.github\workflows\action.yml + Directory.Build.targets = Directory.Build.targets + ..\README.md = ..\README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestNuget", "TestNuget\TestNuget.csproj", "{E7CF6623-54D8-4A40-A1B1-CE078551EFE1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + DebugGenerator|Any CPU = DebugGenerator|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFDAB373-7268-4A1B-ABAF-EA076C3B2B4C}.Release|Any CPU.Build.0 = Release|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA3F5AAE-44B7-4F33-8152-A5251B51AD79}.Release|Any CPU.Build.0 = Release|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.DebugGenerator|Any CPU.ActiveCfg = DebugGenerator|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.DebugGenerator|Any CPU.Build.0 = DebugGenerator|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E267C5B3-44DD-4D26-A2F7-5FDF92FF28D5}.Release|Any CPU.Build.0 = Release|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.DebugGenerator|Any CPU.ActiveCfg = Debug|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.DebugGenerator|Any CPU.Build.0 = Debug|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7CF6623-54D8-4A40-A1B1-CE078551EFE1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B144CF18-21D8-4CB6-9847-DDE7451543D4} + EndGlobalSection +EndGlobal diff --git a/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs b/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs index 14b2c44..ae41c71 100644 --- a/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs +++ b/AutomaticInterface/AutomaticInterface/AutomaticInterfaceGenerator.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Immutable; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; namespace AutomaticInterface; @@ -13,6 +15,25 @@ public class AutomaticInterfaceGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { + context.RegisterPostInitializationOutput(static postInitializationContext => { + postInitializationContext.AddSource($"{DefaultAttributeName}.Attribute.g.cs", SourceText.From($$$""" + // + using System; + + namespace AutomaticInterface + { + /// + /// Use source generator to automatically create a Interface from this class + /// + [AttributeUsage(AttributeTargets.Class)] + internal sealed class {{{DefaultAttributeName}}}Attribute : Attribute + { + internal {{{DefaultAttributeName}}}Attribute(string namespaceName = "") { } + } + } + """, Encoding.UTF8)); + }); + var classes = context .SyntaxProvider.CreateSyntaxProvider(CouldBeClassWithInterfaceAttribute, Transform) .Where(type => type is not null) diff --git a/AutomaticInterface/AutomaticInterfaceAttribute/AutomaticInterfaceAttribute.csproj b/AutomaticInterface/AutomaticInterfaceAttribute/AutomaticInterfaceAttribute.csproj deleted file mode 100644 index 7d4bc02..0000000 --- a/AutomaticInterface/AutomaticInterfaceAttribute/AutomaticInterfaceAttribute.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - netstandard2.0 - Debug;Release;DebugGenerator - - - diff --git a/AutomaticInterface/AutomaticInterfaceAttribute/GenerateAutomaticInterfaceAttribute.cs b/AutomaticInterface/AutomaticInterfaceAttribute/GenerateAutomaticInterfaceAttribute.cs deleted file mode 100644 index 84133f1..0000000 --- a/AutomaticInterface/AutomaticInterfaceAttribute/GenerateAutomaticInterfaceAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace AutomaticInterfaceAttribute -{ - [AttributeUsage(AttributeTargets.Class)] - public class GenerateAutomaticInterfaceAttribute : Attribute - { - public GenerateAutomaticInterfaceAttribute(string namespaceName = "") { } - } -} diff --git a/AutomaticInterface/AutomaticInterfaceExample/AutomaticInterfaceExample.csproj b/AutomaticInterface/AutomaticInterfaceExample/AutomaticInterfaceExample.csproj index 22871ae..4e02364 100644 --- a/AutomaticInterface/AutomaticInterfaceExample/AutomaticInterfaceExample.csproj +++ b/AutomaticInterface/AutomaticInterfaceExample/AutomaticInterfaceExample.csproj @@ -12,7 +12,6 @@ - diff --git a/AutomaticInterface/AutomaticInterfaceExample/DemoClass.cs b/AutomaticInterface/AutomaticInterfaceExample/DemoClass.cs index af4950d..e520807 100644 --- a/AutomaticInterface/AutomaticInterfaceExample/DemoClass.cs +++ b/AutomaticInterface/AutomaticInterfaceExample/DemoClass.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using AutomaticInterfaceAttribute; +using AutomaticInterface; namespace AutomaticInterfaceExample { diff --git a/AutomaticInterface/AutomaticInterfaceExample/DemoClassWithCustomNamespace.cs b/AutomaticInterface/AutomaticInterfaceExample/DemoClassWithCustomNamespace.cs index f0ea959..cb61b27 100644 --- a/AutomaticInterface/AutomaticInterfaceExample/DemoClassWithCustomNamespace.cs +++ b/AutomaticInterface/AutomaticInterfaceExample/DemoClassWithCustomNamespace.cs @@ -1,5 +1,5 @@ using System; -using AutomaticInterfaceAttribute; +using AutomaticInterface; using CustomNamespace; namespace AutomaticInterfaceExample diff --git a/AutomaticInterface/AutomaticInterfaceExample/InterfaceExample.cs b/AutomaticInterface/AutomaticInterfaceExample/InterfaceExample.cs index 32d5d4b..1048975 100644 --- a/AutomaticInterface/AutomaticInterfaceExample/InterfaceExample.cs +++ b/AutomaticInterface/AutomaticInterfaceExample/InterfaceExample.cs @@ -1,7 +1,7 @@ #nullable enable using System; using System.CodeDom.Compiler; -using AutomaticInterfaceAttribute; +using AutomaticInterface; namespace AutomaticInterfaceExample { diff --git a/AutomaticInterface/TestNuget/RemoveThisOnNextVersionAttribute.cs b/AutomaticInterface/TestNuget/RemoveThisOnNextVersionAttribute.cs new file mode 100644 index 0000000..1a28c2c --- /dev/null +++ b/AutomaticInterface/TestNuget/RemoveThisOnNextVersionAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace AutomaticInterface +{ + /// + /// Use source generator to automatically create a Interface from this class + /// + [AttributeUsage(AttributeTargets.Class)] + [Obsolete("This attribute is obsolete. Use generated attribute instead.", false)] + internal sealed class GenerateAutomaticInterfaceAttribute : Attribute + { + internal GenerateAutomaticInterfaceAttribute(string namespaceName = "") { } + } +} \ No newline at end of file diff --git a/AutomaticInterface/TestNuget/Test.cs b/AutomaticInterface/TestNuget/Test.cs index f5f67c3..7a0f1c5 100644 --- a/AutomaticInterface/TestNuget/Test.cs +++ b/AutomaticInterface/TestNuget/Test.cs @@ -1,4 +1,4 @@ -using AutomaticInterfaceAttribute; +using AutomaticInterface; namespace TestNuget { diff --git a/AutomaticInterface/TestNuget/TestNuget.csproj b/AutomaticInterface/TestNuget/TestNuget.csproj index 64b97c5..7a0c96c 100644 --- a/AutomaticInterface/TestNuget/TestNuget.csproj +++ b/AutomaticInterface/TestNuget/TestNuget.csproj @@ -12,8 +12,4 @@ - - - - diff --git a/AutomaticInterface/Tests/GeneratorTests.cs b/AutomaticInterface/Tests/GeneratorTests.cs index fe3ff9f..7479ae2 100644 --- a/AutomaticInterface/Tests/GeneratorTests.cs +++ b/AutomaticInterface/Tests/GeneratorTests.cs @@ -38,7 +38,7 @@ out var diagnostics diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).Should().BeEmpty(); - return outputCompilation.SyntaxTrees.Skip(1).LastOrDefault()?.ToString(); + return outputCompilation.SyntaxTrees.Skip(1).LastOrDefault()?.ToString().Replace("\r",""); } [Fact] @@ -1901,17 +1901,11 @@ public void CustomNameSpace() { const string code = """ - using AutomaticInterfaceAttribute; + using AutomaticInterface; using System; namespace AutomaticInterfaceExample { - [AttributeUsage(AttributeTargets.Class)] - public class GenerateAutomaticInterfaceAttribute : Attribute - { - public GenerateAutomaticInterfaceAttribute(string namespaceName = "") { } - } - /// /// Bla bla /// @@ -1946,7 +1940,7 @@ class DemoClass #nullable enable using System.CodeDom.Compiler; - using AutomaticInterfaceAttribute; + using AutomaticInterface; using System; namespace CustomNameSpace diff --git a/AutomaticInterface/Tests/Tests.csproj b/AutomaticInterface/Tests/Tests.csproj index 2173cab..a52c17a 100644 --- a/AutomaticInterface/Tests/Tests.csproj +++ b/AutomaticInterface/Tests/Tests.csproj @@ -20,7 +20,6 @@ - diff --git a/README.md b/README.md index 848452f..658ff8c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This interface will be generated on each subsequent build, eliminating the the f ## Example ```c# -using AutomaticInterfaceAttribute; +using AutomaticInterface; using System; namespace AutomaticInterfaceExample @@ -85,7 +85,7 @@ This will create this interface: ```C# #nullable enable using System.CodeDom.Compiler; -using AutomaticInterfaceAttribute; +using AutomaticInterface; using System; /// @@ -126,9 +126,8 @@ namespace AutomaticInterfaceExample ## How to use it? 1. Install the nuget: `dotnet add package AutomaticInterface` -2. Create an Attribute with the Name `[GenerateAutomaticInterface]`. You can just copy the minimal code from this Repo (see the `AutomaticInterfaceAttribute` project). It's the easiest way to get that attribute because you cannot reference any code from the analyzer package. -3. Let your class implement the interface, e.g. `SomeClass: ISomeClass` -4. Build Solution, the Interface should now be available. +2. Let your class implement the interface, e.g. `SomeClass: ISomeClass` +3. Build Solution, the Interface should now be available. Any errors? Ping me at: christiian.sauer@codecentric.de