From 79c7dcd3b1f1497a091fa4bb5b0b5c71a145e8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sat, 18 Nov 2023 19:31:03 -0500 Subject: [PATCH] Add annotations for Meziantou.Framework.FastEnumToStringGenerator --- Meziantou.Framework.sln | 13 ++- eng/validate-nuget.ps1 | 30 ++++--- .../FastEnumToStringAttribute.cs | 16 ++++ ...stEnumToStringGenerator.Annotations.csproj | 11 +++ .../EnumToStringSourceGenerator.cs | 19 +--- ...Framework.FastEnumToStringGenerator.csproj | 7 +- ...ramework.FastEnumToStringGenerator.targets | 10 +++ .../EnumToStringSourceGeneratorTests.cs | 86 ++++++++++--------- ...ork.FastEnumToStringGenerator.Tests.csproj | 1 + 9 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/FastEnumToStringAttribute.cs create mode 100644 src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/Meziantou.Framework.FastEnumToStringGenerator.Annotations.csproj create mode 100644 src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.targets diff --git a/Meziantou.Framework.sln b/Meziantou.Framework.sln index a793a3d2e..2d5e0bc44 100644 --- a/Meziantou.Framework.sln +++ b/Meziantou.Framework.sln @@ -285,11 +285,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meziantou.Framework.Diagnos EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meziantou.Framework.InlineSnapshotTesting.Samples", "Samples\Meziantou.Framework.InlineSnapshotTesting.Samples\Meziantou.Framework.InlineSnapshotTesting.Samples.csproj", "{7A9F224E-CBA3-4272-97C0-0190B7125AC7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meziantou.Framework.Win32.RecentDocuments", "src\Meziantou.Framework.Win32.RecentDocuments\Meziantou.Framework.Win32.RecentDocuments.csproj", "{F2313EFC-F1BD-49EC-9CC2-0DE2CF0A1CAD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meziantou.Framework.Win32.RecentDocuments", "src\Meziantou.Framework.Win32.RecentDocuments\Meziantou.Framework.Win32.RecentDocuments.csproj", "{F2313EFC-F1BD-49EC-9CC2-0DE2CF0A1CAD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meziantou.Framework.Win32.RecentDocumentsConsole", "Samples\Meziantou.Framework.Win32.RecentDocumentsConsole\Meziantou.Framework.Win32.RecentDocumentsConsole.csproj", "{C35AE5A4-3BD7-43F2-940D-5B0E90BF0FBF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meziantou.Framework.Win32.RecentDocumentsConsole", "Samples\Meziantou.Framework.Win32.RecentDocumentsConsole\Meziantou.Framework.Win32.RecentDocumentsConsole.csproj", "{C35AE5A4-3BD7-43F2-940D-5B0E90BF0FBF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meziantou.Framework.StronglyTypedId.Annotations", "src\Meziantou.Framework.StronglyTypedId.Annotations\Meziantou.Framework.StronglyTypedId.Annotations.csproj", "{25742DFB-FFA4-419A-85B3-F821D607A17C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Meziantou.Framework.StronglyTypedId.Annotations", "src\Meziantou.Framework.StronglyTypedId.Annotations\Meziantou.Framework.StronglyTypedId.Annotations.csproj", "{25742DFB-FFA4-419A-85B3-F821D607A17C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meziantou.Framework.FastEnumToStringGenerator.Annotations", "src\Meziantou.Framework.FastEnumToStringGenerator.Annotations\Meziantou.Framework.FastEnumToStringGenerator.Annotations.csproj", "{67A21035-0C6C-4715-9CD5-260B814E52DA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -801,6 +803,10 @@ Global {25742DFB-FFA4-419A-85B3-F821D607A17C}.Debug|Any CPU.Build.0 = Debug|Any CPU {25742DFB-FFA4-419A-85B3-F821D607A17C}.Release|Any CPU.ActiveCfg = Release|Any CPU {25742DFB-FFA4-419A-85B3-F821D607A17C}.Release|Any CPU.Build.0 = Release|Any CPU + {67A21035-0C6C-4715-9CD5-260B814E52DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67A21035-0C6C-4715-9CD5-260B814E52DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67A21035-0C6C-4715-9CD5-260B814E52DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67A21035-0C6C-4715-9CD5-260B814E52DA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -933,6 +939,7 @@ Global {F2313EFC-F1BD-49EC-9CC2-0DE2CF0A1CAD} = {70A3E965-3866-4D23-9626-8D70E0E8729C} {C35AE5A4-3BD7-43F2-940D-5B0E90BF0FBF} = {DB679A70-C2CB-4CD1-9530-D31627343FC5} {25742DFB-FFA4-419A-85B3-F821D607A17C} = {70A3E965-3866-4D23-9626-8D70E0E8729C} + {67A21035-0C6C-4715-9CD5-260B814E52DA} = {70A3E965-3866-4D23-9626-8D70E0E8729C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AA866021-BF24-4172-A655-F0E8316C38BA} diff --git a/eng/validate-nuget.ps1 b/eng/validate-nuget.ps1 index 61beb6c6c..20df2fca8 100644 --- a/eng/validate-nuget.ps1 +++ b/eng/validate-nuget.ps1 @@ -1,22 +1,30 @@ pwsh --version # $env:NuGetDirectory= Join-Path $PSScriptRoot "../artifacts/package/debug" -Resolve -$PackagePath = (Get-ChildItem $env:NuGetDirectory | Where-Object FullName -Match "Meziantou.Framework.StronglyTypedId.[0-9.]+.nupkg").FullName -$AnnotationPath = Join-Path $PSScriptRoot ".." "src" "Meziantou.Framework.StronglyTypedId.Annotations" -Resolve +$Generators = @("Meziantou.Framework.StronglyTypedId", "Meziantou.Framework.FastEnumToStringGenerator") +foreach ($Generator in $Generators) { + Write-Host "Checking $Generator" -$Tfms = $(dotnet build --getProperty:TargetFrameworks $AnnotationPath).Split(";") + $PackagePath = (Get-ChildItem $env:NuGetDirectory | Where-Object FullName -Match "$Generator.[0-9.-]+.nupkg").FullName + $AnnotationPath = Join-Path $PSScriptRoot ".." "src" "$Generator.Annotations" -Resolve -[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') -$Entries = [IO.Compression.ZipFile]::OpenRead($PackagePath).Entries.FullName -foreach ($Tfm in $Tfms) { - # Check if there is an entry with a path that starts with "lib/$Tfm/" - $Entry = $Entries | Where-Object { $_.StartsWith("lib/$Tfm/") } - if (-not $Entry) { - Write-Error "Package does not contain a lib/$Tfm/ entry" - exit 1 + $Tfms = $(dotnet build --getProperty:TargetFrameworks $AnnotationPath).Split(";") + + [Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') + $ZipFile = [IO.Compression.ZipFile]::OpenRead($PackagePath) + $Entries = $ZipFile.Entries.FullName + $ZipFile.Dispose() + foreach ($Tfm in $Tfms) { + # Check if there is an entry with a path that starts with "lib/$Tfm/" + $Entry = $Entries | Where-Object { $_.StartsWith("lib/$Tfm/") } + if (-not $Entry) { + Write-Error "Package does not contain a lib/$Tfm/ entry" + exit 1 + } } } +Write-Host "Validating NuGet packages" dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global --no-cache --add-source $env:NuGetDirectory $files = Get-ChildItem "$env:NuGetDirectory/*" -Include *.nupkg diff --git a/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/FastEnumToStringAttribute.cs b/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/FastEnumToStringAttribute.cs new file mode 100644 index 000000000..482d22ebf --- /dev/null +++ b/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/FastEnumToStringAttribute.cs @@ -0,0 +1,16 @@ +namespace Meziantou.Framework.Annotations; + +[System.Diagnostics.Conditional("FastEnumToString_Attributes")] +[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] +public sealed class FastEnumToStringAttribute : Attribute +{ + public FastEnumToStringAttribute(Type enumType) + { + EnumType = enumType; + } + + public bool IsPublic { get; set; } = true; + public string? ExtensionMethodNamespace { get; set; } + + public Type EnumType { get; } +} diff --git a/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/Meziantou.Framework.FastEnumToStringGenerator.Annotations.csproj b/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/Meziantou.Framework.FastEnumToStringGenerator.Annotations.csproj new file mode 100644 index 000000000..22c658dc5 --- /dev/null +++ b/src/Meziantou.Framework.FastEnumToStringGenerator.Annotations/Meziantou.Framework.FastEnumToStringGenerator.Annotations.csproj @@ -0,0 +1,11 @@ + + + $(LatestTargetFrameworks);netstandard2.0 + Attributes for the Meziantou.Framework.FastEnumToStringGenerator source generator + false + + + + + + diff --git a/src/Meziantou.Framework.FastEnumToStringGenerator/EnumToStringSourceGenerator.cs b/src/Meziantou.Framework.FastEnumToStringGenerator/EnumToStringSourceGenerator.cs index 61cd35c73..37e58d087 100644 --- a/src/Meziantou.Framework.FastEnumToStringGenerator/EnumToStringSourceGenerator.cs +++ b/src/Meziantou.Framework.FastEnumToStringGenerator/EnumToStringSourceGenerator.cs @@ -10,25 +10,8 @@ namespace Meziantou.Framework.FastEnumToStringGenerator; [Generator] public sealed partial class EnumToStringSourceGenerator : IIncrementalGenerator { - [SuppressMessage("Usage", "MA0101:String contains an implicit end of line character", Justification = "Not important")] - private const string AttributeText = @" -#nullable enable -[System.Diagnostics.Conditional(""FastEnumToString_Attributes"")] -[System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] -internal sealed class FastEnumToStringAttribute : System.Attribute -{ - public FastEnumToStringAttribute(System.Type enumType) - { - } - - public bool IsPublic { get; set; } = true; - public string? ExtensionMethodNamespace { get; set; } -} -"; - public void Initialize(IncrementalGeneratorInitializationContext context) { - context.RegisterPostInitializationOutput(ctx => ctx.AddSource("FastEnumToStringAttribute.g.cs", SourceText.From(AttributeText, Encoding.UTF8))); var enums = context.SyntaxProvider.CreateSyntaxProvider( predicate: static (syntax, cancellationToken) => IsSyntaxTargetForGeneration(syntax), transform: static (ctx, cancellationToken) => GetSemanticTargetForGeneration(ctx, cancellationToken)) @@ -50,7 +33,7 @@ static bool IsSyntaxTargetForGeneration(SyntaxNode syntax) static EnumToProcess? GetSemanticTargetForGeneration(GeneratorSyntaxContext ctx, CancellationToken cancellationToken) { var compilation = ctx.SemanticModel.Compilation; - var fastEnumToStringAttributeSymbol = compilation.GetTypeByMetadataName("FastEnumToStringAttribute"); + var fastEnumToStringAttributeSymbol = compilation.GetTypeByMetadataName("Meziantou.Framework.Annotations.FastEnumToStringAttribute"); if (fastEnumToStringAttributeSymbol is null) return null; diff --git a/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.csproj b/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.csproj index 95f912e7a..ffaa7404f 100644 --- a/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.csproj +++ b/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.csproj @@ -2,7 +2,7 @@ $(LatestTargetFrameworks);netstandard2.0 true - 1.0.8 + 2.0.0 Generate a faster ToString method for enumerations true @@ -12,7 +12,12 @@ + + + + + diff --git a/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.targets b/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.targets new file mode 100644 index 000000000..a32e6161b --- /dev/null +++ b/src/Meziantou.Framework.FastEnumToStringGenerator/Meziantou.Framework.FastEnumToStringGenerator.targets @@ -0,0 +1,10 @@ + + + $(ImplicitUsings) + + + + + + + diff --git a/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/EnumToStringSourceGeneratorTests.cs b/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/EnumToStringSourceGeneratorTests.cs index 1c645dae6..abd1ab1b1 100644 --- a/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/EnumToStringSourceGeneratorTests.cs +++ b/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/EnumToStringSourceGeneratorTests.cs @@ -2,6 +2,7 @@ using System.Reflection; using FluentAssertions; using FluentAssertions.Execution; +using Meziantou.Framework.Annotations; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using TestUtilities; @@ -13,10 +14,11 @@ public sealed class EnumToStringSourceGeneratorTests { private static async Task<(GeneratorDriverRunResult GeneratorResult, Compilation OutputCompilation, byte[] Assembly)> GenerateFiles(string file, bool mustCompile = true, string[] assemblyLocations = null) { - var netcoreRef = await NuGetHelpers.GetNuGetReferences("Microsoft.NETCore.App.Ref", "5.0.0", "ref/net5.0/"); + var netcoreRef = await NuGetHelpers.GetNuGetReferences("Microsoft.NETCore.App.Ref", "6.0.0", "ref/net6.0/"); assemblyLocations ??= []; var references = assemblyLocations .Concat(netcoreRef) + .Append(typeof(FastEnumToStringAttribute).Assembly.Location) .Select(loc => MetadataReference.CreateFromFile(loc)) .ToArray(); @@ -41,7 +43,7 @@ public sealed class EnumToStringSourceGeneratorTests if (mustCompile) { var diags = string.Join('\n', result.Diagnostics); - var generated = (await runResult.GeneratedTrees[1].GetRootAsync()).ToFullString(); + var generated = (await runResult.GeneratedTrees[0].GetRootAsync()).ToFullString(); result.Success.Should().BeTrue("Project should build build:\n" + diags + "\n\n\n" + generated); result.Diagnostics.Should().BeEmpty(); } @@ -52,28 +54,29 @@ public sealed class EnumToStringSourceGeneratorTests [Fact] public async Task GenerateStructInNamespaceAndClass() { - var sourceCode = @" -[assembly: FastEnumToStringAttribute(typeof(A.B.C.D))] -namespace A -{ - namespace B - { - class C - { - public static string Sample(D value) => value.ToStringFast(); - - public enum D + var sourceCode = """ + [assembly: Meziantou.Framework.Annotations.FastEnumToStringAttribute(typeof(A.B.C.D))] + namespace A { - Value1, - Value2, + namespace B + { + class C + { + public static string Sample(D value) => value.ToStringFast(); + + public enum D + { + Value1, + Value2, + } + } + } } - } - } -}"; + """; var (generatorResult, _, assembly) = await GenerateFiles(sourceCode); generatorResult.Diagnostics.Should().BeEmpty(); - generatorResult.GeneratedTrees.Length.Should().Be(2); + generatorResult.GeneratedTrees.Length.Should().Be(1); var asm = Assembly.Load(assembly); var type = asm.GetType("A.B.C"); @@ -86,33 +89,34 @@ public enum D [Fact] public async Task GeneratePublicType() { - var sourceCode = @" -using SampleNs1; - -[assembly: FastEnumToStringAttribute(typeof(A.B.D), IsPublic = true, ExtensionMethodNamespace = ""SampleNs1"")] -[assembly: FastEnumToStringAttribute(typeof(A.B.E), IsPublic = false, ExtensionMethodNamespace = ""SampleNs1"")] -[assembly: FastEnumToStringAttribute(typeof(A.B.F), ExtensionMethodNamespace = ""SampleNs3"")] -[assembly: FastEnumToStringAttribute(typeof(A.B.G), ExtensionMethodNamespace = ""SampleNs4"")] + var sourceCode = """ + using SampleNs1; -namespace A -{ - namespace B - { - public class C - { - public static string Sample(D value) => value.ToStringFast(); - } + [assembly: Meziantou.Framework.Annotations.FastEnumToStringAttribute(typeof(A.B.D), IsPublic = true, ExtensionMethodNamespace = "SampleNs1")] + [assembly: Meziantou.Framework.Annotations.FastEnumToStringAttribute(typeof(A.B.E), IsPublic = false, ExtensionMethodNamespace = "SampleNs1")] + [assembly: Meziantou.Framework.Annotations.FastEnumToStringAttribute(typeof(A.B.F), ExtensionMethodNamespace = "SampleNs3")] + [assembly: Meziantou.Framework.Annotations.FastEnumToStringAttribute(typeof(A.B.G), ExtensionMethodNamespace = "SampleNs4")] - public enum D { Value1 } - public enum E { Value1 } - internal enum F { Value1 } - public enum G { Value1 } - } -}"; + namespace A + { + namespace B + { + public class C + { + public static string Sample(D value) => value.ToStringFast(); + } + + public enum D { Value1 } + public enum E { Value1 } + internal enum F { Value1 } + public enum G { Value1 } + } + } + """; var (generatorResult, _, assembly) = await GenerateFiles(sourceCode); generatorResult.Diagnostics.Should().BeEmpty(); - generatorResult.GeneratedTrees.Length.Should().Be(2); + generatorResult.GeneratedTrees.Length.Should().Be(1); var asm = Assembly.Load(assembly); var ns1Type = asm.GetType("SampleNs1.FastEnumToStringExtensions"); diff --git a/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/Meziantou.Framework.FastEnumToStringGenerator.Tests.csproj b/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/Meziantou.Framework.FastEnumToStringGenerator.Tests.csproj index 94f0db073..5385c17c0 100644 --- a/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/Meziantou.Framework.FastEnumToStringGenerator.Tests.csproj +++ b/tests/Meziantou.Framework.FastEnumToStringGenerator.Tests/Meziantou.Framework.FastEnumToStringGenerator.Tests.csproj @@ -5,6 +5,7 @@ +