From d45ee3ca4794de556876600c37cd7a0d1d24b315 Mon Sep 17 00:00:00 2001 From: ttd2089 Date: Sun, 24 Sep 2023 22:38:07 -0300 Subject: [PATCH] wip: create generator project Creates a generator project that catches all references to generic names and generates warnings. This is the first step in catching references to the AddDefaultParser generic method and replacing them with calls to add explicitly constructed parsers that use either the TryParse or string constructors on the associated type. This avoids the need to use reflection and makes the code safer for trimming, as well as allowing us to raise an error if the type doesn't support a default parser. --- .gitignore | 3 ++ src/Geis.ScratchApp/Geis.ScratchApp.csproj | 17 +++++++ src/Geis.ScratchApp/Program.cs | 5 ++ .../DefaultValueParserGenerator.cs | 49 +++++++++++++++++++ .../Geis.ValueParserGenerators.csproj | 17 +++++++ src/Geis.sln | 37 ++++++++++++++ src/Geis/Geis.csproj | 9 ++++ src/Geis/ValueParserCollection.cs | 17 +++++++ 8 files changed, 154 insertions(+) create mode 100644 .gitignore create mode 100644 src/Geis.ScratchApp/Geis.ScratchApp.csproj create mode 100644 src/Geis.ScratchApp/Program.cs create mode 100644 src/Geis.ValueParserGenerators/DefaultValueParserGenerator.cs create mode 100644 src/Geis.ValueParserGenerators/Geis.ValueParserGenerators.csproj create mode 100644 src/Geis.sln create mode 100644 src/Geis/Geis.csproj create mode 100644 src/Geis/ValueParserCollection.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ff6da7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs/ +[Bb]in/ +[Oo]bj/ diff --git a/src/Geis.ScratchApp/Geis.ScratchApp.csproj b/src/Geis.ScratchApp/Geis.ScratchApp.csproj new file mode 100644 index 0000000..e432de9 --- /dev/null +++ b/src/Geis.ScratchApp/Geis.ScratchApp.csproj @@ -0,0 +1,17 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + diff --git a/src/Geis.ScratchApp/Program.cs b/src/Geis.ScratchApp/Program.cs new file mode 100644 index 0000000..667291c --- /dev/null +++ b/src/Geis.ScratchApp/Program.cs @@ -0,0 +1,5 @@ +using Geis; + +var collection = new ValueParserCollection(); +collection.AddDefaultParser(); +collection.AddDefaultParser(); diff --git a/src/Geis.ValueParserGenerators/DefaultValueParserGenerator.cs b/src/Geis.ValueParserGenerators/DefaultValueParserGenerator.cs new file mode 100644 index 0000000..69a9d7e --- /dev/null +++ b/src/Geis.ValueParserGenerators/DefaultValueParserGenerator.cs @@ -0,0 +1,49 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Immutable; + +namespace Geis.ValueParserGenerators; + +[Generator] +public class DefaultValueParserGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + //System.Diagnostics.Debugger.Launch(); + + var provider = context + .SyntaxProvider + .CreateSyntaxProvider( + predicate: (c, _) => c is GenericNameSyntax n, + transform: (n, _) => (GenericNameSyntax)n.Node) + .Where(x => x is not null); + + var compilation = context.CompilationProvider.Combine(provider.Collect()); + + context.RegisterSourceOutput( + compilation, + (spc, source) => Execute(spc, source.Left, source.Right)); + } + + private void Execute( + SourceProductionContext context, + Compilation compilation, + ImmutableArray genericNames) + { + foreach (var name in genericNames) + { + // todo: add explicitly constructor value parsers that reference the TryParse or new(string) member of T. + + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + id: "GVPG0001", + title: "Possible Reference To AddDefaultParser", + messageFormat: "Found a possible reference to the AddDefaultParser method.", + category: "Accomplishment", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true), + name.GetLocation())); + } + } +} diff --git a/src/Geis.ValueParserGenerators/Geis.ValueParserGenerators.csproj b/src/Geis.ValueParserGenerators/Geis.ValueParserGenerators.csproj new file mode 100644 index 0000000..f8e9e10 --- /dev/null +++ b/src/Geis.ValueParserGenerators/Geis.ValueParserGenerators.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + latest + true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/src/Geis.sln b/src/Geis.sln new file mode 100644 index 0000000..1f6ed85 --- /dev/null +++ b/src/Geis.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geis.ValueParserGenerators", "Geis.ValueParserGenerators\Geis.ValueParserGenerators.csproj", "{388C1697-D0ED-4340-AE58-22CF614FD50D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geis", "Geis\Geis.csproj", "{8B1B4AA8-17E8-4A0C-946B-FD975A2598EA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geis.ScratchApp", "Geis.ScratchApp\Geis.ScratchApp.csproj", "{435D5879-9BB7-4106-BB3A-2B22750C08EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {388C1697-D0ED-4340-AE58-22CF614FD50D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {388C1697-D0ED-4340-AE58-22CF614FD50D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {388C1697-D0ED-4340-AE58-22CF614FD50D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {388C1697-D0ED-4340-AE58-22CF614FD50D}.Release|Any CPU.Build.0 = Release|Any CPU + {8B1B4AA8-17E8-4A0C-946B-FD975A2598EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B1B4AA8-17E8-4A0C-946B-FD975A2598EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B1B4AA8-17E8-4A0C-946B-FD975A2598EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B1B4AA8-17E8-4A0C-946B-FD975A2598EA}.Release|Any CPU.Build.0 = Release|Any CPU + {435D5879-9BB7-4106-BB3A-2B22750C08EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {435D5879-9BB7-4106-BB3A-2B22750C08EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {435D5879-9BB7-4106-BB3A-2B22750C08EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {435D5879-9BB7-4106-BB3A-2B22750C08EF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3043616A-5042-4512-816E-3345E1FA7AB3} + EndGlobalSection +EndGlobal diff --git a/src/Geis/Geis.csproj b/src/Geis/Geis.csproj new file mode 100644 index 0000000..ad4f58d --- /dev/null +++ b/src/Geis/Geis.csproj @@ -0,0 +1,9 @@ + + + + net7 + enable + enable + + + diff --git a/src/Geis/ValueParserCollection.cs b/src/Geis/ValueParserCollection.cs new file mode 100644 index 0000000..b68b971 --- /dev/null +++ b/src/Geis/ValueParserCollection.cs @@ -0,0 +1,17 @@ +namespace Geis; + +public class ValueParserCollection +{ + private readonly Dictionary> _parsers = new(); + + public ValueParserCollection SetParser(Func parser) where T : notnull + { + _parsers[typeof(T)] = s => parser(s); + return this; + } +} + +public static class ValueParserCollectionExtensions +{ + public static ValueParserCollection AddDefaultParser(this ValueParserCollection collection) => collection; +}