From 141c9eaaf83e85848001848a237d0e12b97b5b9e Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Mon, 13 Nov 2023 15:04:29 -0500 Subject: [PATCH 01/26] Create enum for valid collection types --- .../SourceGenerationHelper.cs | 12 ++++++++++++ .../GenerationSandbox.Tests.csproj | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index a42cf50..0ac1db6 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -5,6 +5,18 @@ /// public static class SourceGenerationHelper { + internal const string CreateCollectionEnumSource = """ + [Flags] + public enum CollectionTypes + { + IList, + ReadOnlySpan, + Span, + IEnumerable + } + + """; + internal const string CreateSyncVersionAttributeSource = """ // namespace Zomp.SyncMethodGenerator diff --git a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj index d5d2288..d55f488 100644 --- a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj +++ b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj @@ -1,7 +1,7 @@  - $(NoWarn);CA1812;CA1852;SA1402;CA1822;SA1201;CA1303;SA1124;IDE0035;CS0162;RS1035;CS8619;CS8603;CA5394 + $(NoWarn);CA1812;CA1852;SA1402;CA1822;SA1201;CA1303;SA1124;IDE0035;CS0162;RS1035;SA1601;CS8619;CS8603;CA5394 false net7.0;net6.0 $(TargetFrameworks);net472 From 1c44e77c86d28b6d3b8aa0a761bfd7ae1e1dc860 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Mon, 13 Nov 2023 15:23:11 -0500 Subject: [PATCH 02/26] Add a property for variations --- .../SourceGenerationHelper.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 0ac1db6..92bef59 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -5,28 +5,32 @@ /// public static class SourceGenerationHelper { - internal const string CreateCollectionEnumSource = """ - [Flags] - public enum CollectionTypes - { - IList, - ReadOnlySpan, - Span, - IEnumerable - } - - """; - internal const string CreateSyncVersionAttributeSource = """ // namespace Zomp.SyncMethodGenerator { + [Flags] + public enum CollectionTypes + { + IList, + ReadOnlySpan, + Span, + IEnumerable + } + /// /// An attribute that can be used to automatically generate a synchronous version of an async method. Must be used in a partial class. /// [System.AttributeUsage(System.AttributeTargets.Method)] internal class CreateSyncVersionAttribute : System.Attribute { + private CollectionTypes variations; + + public virtual CollectionTypes Variations + { + get { return variations; } + set { variations = value; } + } } } """; From 8d360dab94b09011edb29b1c628c662394cdbfbb Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Mon, 13 Nov 2023 16:29:56 -0500 Subject: [PATCH 03/26] Assign values so property can use or flags --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 92bef59..dd64280 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -12,10 +12,10 @@ namespace Zomp.SyncMethodGenerator [Flags] public enum CollectionTypes { - IList, - ReadOnlySpan, - Span, - IEnumerable + IList = 1, + ReadOnlySpan = 2, + Span = 4, + IEnumerable = 8 } /// From de71e8e15b0c3d44ddd7d16ce9c2d3e3ebd5ff94 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 09:42:30 -0500 Subject: [PATCH 04/26] Add comments to pass style checking and include System --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index dd64280..35cc6c6 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -7,14 +7,22 @@ public static class SourceGenerationHelper { internal const string CreateSyncVersionAttributeSource = """ // +using System; namespace Zomp.SyncMethodGenerator { + /// + /// All types that an IAsyncEnumerable can be converted into + /// [Flags] public enum CollectionTypes { + /// IList IList = 1, + /// ReadOnlySpan ReadOnlySpan = 2, + /// Span Span = 4, + /// IEnumerable IEnumerable = 8 } From 3171bb0a1c4000f89d5314b28e96158057788904 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 11:09:48 -0500 Subject: [PATCH 05/26] Provide async rewriter with dictionary of overrides to the typical replacements (chose this design as I thought it could be reusable if other types are given options at any point) --- src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs | 7 +++++-- src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs index fb8731c..cc5e3d9 100644 --- a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs +++ b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs @@ -9,7 +9,8 @@ namespace Zomp.SyncMethodGenerator; /// Creates a new instance of . /// /// The semantic model. -internal sealed class AsyncToSyncRewriter(SemanticModel semanticModel) : CSharpSyntaxRewriter +/// The type of collection that should be used to replace IAsyncEnumerable. +internal sealed class AsyncToSyncRewriter(SemanticModel semanticModel, Dictionary replacementOverrides) : CSharpSyntaxRewriter { public const string SyncOnly = "SYNC_ONLY"; private const string SystemObject = "object"; @@ -213,7 +214,9 @@ static BinaryExpressionSyntax CheckNull(ExpressionSyntax expr) => BinaryExpressi var genericName = GetNameWithoutTypeParams(symbol); string? newType = null; - if (Replacements.TryGetValue(genericName, out var replacement)) + + if (replacementOverrides.TryGetValue(genericName, out var replacement) || + Replacements.TryGetValue(genericName, out replacement)) { if (replacement is not null) { diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index fae9618..bcf8dd8 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -93,6 +93,8 @@ private static void Execute(Compilation compilation, ImmutableArray GetTypesToGenerate(SourceProductionContext context, Compilation compilation, IEnumerable methodDeclarations, CancellationToken ct) { var methodsToGenerate = new List(); + var replacementOverrides = new Dictionary(); + INamedTypeSymbol? attribute = compilation.GetTypeByMetadataName(QualifiedCreateSyncVersionAttribute); if (attribute == null) { @@ -161,7 +163,8 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - var rewriter = new AsyncToSyncRewriter(semanticModel); + //fill out replacement overrides dictionary + var rewriter = new AsyncToSyncRewriter(semanticModel, replacementOverrides); var sn = rewriter.Visit(methodDeclarationSyntax); var content = sn.ToFullString(); From 76a3101542de3cd4787290c45e6c5141242b3a93 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 12:11:13 -0500 Subject: [PATCH 06/26] Add variation namespaces and retireve value from attributedata --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 8 ++++---- src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 35cc6c6..7adaaf9 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -17,13 +17,13 @@ namespace Zomp.SyncMethodGenerator public enum CollectionTypes { /// IList - IList = 1, + System.Collections.Generic.IList = 1, /// ReadOnlySpan - ReadOnlySpan = 2, + System.ReadOnlySpan = 2, /// Span - Span = 4, + System.Span = 4, /// IEnumerable - IEnumerable = 8 + System.Collections.Generic.IEnumerable = 8 } /// diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index bcf8dd8..670b64d 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -129,6 +129,7 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } + var variations = attributeData.NamedArguments[0].Value.Value; break; } From ead92c4906421b0fa896e3326390c9b52f2fb0c5 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 12:54:50 -0500 Subject: [PATCH 07/26] Add enum to library so it can be used in generator --- .../CollectionTypes.cs | 28 +++++++++++++++++++ .../SourceGenerationHelper.cs | 8 +++--- .../SyncMethodSourceGenerator.cs | 9 +++++- tests/Generator.Tests/IntegrationTesting.cs | 2 +- 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 src/Zomp.SyncMethodGenerator/CollectionTypes.cs diff --git a/src/Zomp.SyncMethodGenerator/CollectionTypes.cs b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs new file mode 100644 index 0000000..58be38c --- /dev/null +++ b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs @@ -0,0 +1,28 @@ +namespace Zomp.SyncMethodGenerator; + +/// +/// All types that an IAsyncEnumerable can be converted into. +/// +[Flags] +public enum CollectionTypes +{ + /// + /// Type for System.Collections.Generic.IList . + /// + IList = 1, + + /// + /// Type for System.ReadOnlySpan . + /// + ReadOnlySpan = 2, + + /// + /// Type for System.Span . + /// + Span = 4, + + /// + /// Type for System.Collections.Generic.IEnumerable . + /// + IEnumerable = 8, +} diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 7adaaf9..89545f1 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -17,13 +17,13 @@ namespace Zomp.SyncMethodGenerator public enum CollectionTypes { /// IList - System.Collections.Generic.IList = 1, + IList = 1, /// ReadOnlySpan - System.ReadOnlySpan = 2, + ReadOnlySpan = 2, /// Span - System.Span = 4, + Span = 4, /// IEnumerable - System.Collections.Generic.IEnumerable = 8 + IEnumerable = 8 } /// diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 670b64d..991f76f 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -122,6 +122,7 @@ private static List GetTypesToGenerate(SourceProductionContext var methodName = methodSymbol.ToString(); + var variations = 0; foreach (AttributeData attributeData in methodSymbol.GetAttributes()) { if (!attribute.Equals(attributeData.AttributeClass, SymbolEqualityComparer.Default)) @@ -129,7 +130,11 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - var variations = attributeData.NamedArguments[0].Value.Value; + if (attributeData.NamedArguments[0].Value.Value is int value) + { + variations = value; + } + break; } @@ -164,6 +169,8 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } + var @collections = (CollectionTypes)variations; + //fill out replacement overrides dictionary var rewriter = new AsyncToSyncRewriter(semanticModel, replacementOverrides); var sn = rewriter.Visit(methodDeclarationSyntax); diff --git a/tests/Generator.Tests/IntegrationTesting.cs b/tests/Generator.Tests/IntegrationTesting.cs index 19cddeb..3ced161 100644 --- a/tests/Generator.Tests/IntegrationTesting.cs +++ b/tests/Generator.Tests/IntegrationTesting.cs @@ -56,7 +56,7 @@ async Task EnumeratorTestAsync(IAsyncEnumerable range, CancellationToken ct [Fact] public Task CombineTwoLists() => """ -[CreateSyncVersion] +[CreateSyncVersion(Variations = (CollectionTypes)5)] public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) { await using var enumerator2 = list2.GetAsyncEnumerator(ct); From 4bd3631fd86771c4041370d8165a08afe4de739c Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 14:45:10 -0500 Subject: [PATCH 08/26] Check property to find which variants have been chosen --- .../SyncMethodSourceGenerator.cs | 85 ++++++++++++------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 991f76f..4088155 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -93,7 +93,6 @@ private static void Execute(Compilation compilation, ImmutableArray GetTypesToGenerate(SourceProductionContext context, Compilation compilation, IEnumerable methodDeclarations, CancellationToken ct) { var methodsToGenerate = new List(); - var replacementOverrides = new Dictionary(); INamedTypeSymbol? attribute = compilation.GetTypeByMetadataName(QualifiedCreateSyncVersionAttribute); if (attribute == null) @@ -130,7 +129,7 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - if (attributeData.NamedArguments[0].Value.Value is int value) + if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is int value) { variations = value; } @@ -169,48 +168,74 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - var @collections = (CollectionTypes)variations; + var collections = new List(); - //fill out replacement overrides dictionary - var rewriter = new AsyncToSyncRewriter(semanticModel, replacementOverrides); - var sn = rewriter.Visit(methodDeclarationSyntax); - var content = sn.ToFullString(); + if (((CollectionTypes)variations & CollectionTypes.IList) == CollectionTypes.IList) + { + collections.Add("System.Collections.Generic.IList"); + } - var diagnostics = rewriter.Diagnostics; + if (((CollectionTypes)variations & CollectionTypes.Span) == CollectionTypes.IList) + { + collections.Add("System.Span"); + } - var hasErrors = false; - foreach (var diagnostic in diagnostics) + if (((CollectionTypes)variations & CollectionTypes.ReadOnlySpan) == CollectionTypes.IList) { - context.ReportDiagnostic(diagnostic); - hasErrors |= diagnostic.Severity == DiagnosticSeverity.Error; + collections.Add("System.ReadOnlySpan"); } - if (hasErrors) + if (((CollectionTypes)variations & CollectionTypes.IEnumerable) == CollectionTypes.IList) { - continue; + collections.Add("System.Collections.Generic.IEnumerable"); } - var isNamespaceFileScoped = false; - var namespaces = new List(); - while (node is not null && node is not CompilationUnitSyntax) + foreach (var collection in collections) { - switch (node) + var replacementOverrides = new Dictionary { - case NamespaceDeclarationSyntax nds: - namespaces.Insert(0, nds.Name.ToString()); - break; - case FileScopedNamespaceDeclarationSyntax file: - namespaces.Add(file.Name.ToString()); - isNamespaceFileScoped = true; - break; - default: - throw new InvalidOperationException($"Cannot handle {node}"); + { "System.Collections.Generic.IAsyncEnumerable", collection }, + }; + var rewriter = new AsyncToSyncRewriter(semanticModel, replacementOverrides); + var sn = rewriter.Visit(methodDeclarationSyntax); + var content = sn.ToFullString(); + + var diagnostics = rewriter.Diagnostics; + + var hasErrors = false; + foreach (var diagnostic in diagnostics) + { + context.ReportDiagnostic(diagnostic); + hasErrors |= diagnostic.Severity == DiagnosticSeverity.Error; } - node = node.Parent; - } + if (hasErrors) + { + continue; + } - methodsToGenerate.Add(new(namespaces, isNamespaceFileScoped, classes, methodDeclarationSyntax.Identifier.ValueText, content)); + var isNamespaceFileScoped = false; + var namespaces = new List(); + while (node is not null && node is not CompilationUnitSyntax) + { + switch (node) + { + case NamespaceDeclarationSyntax nds: + namespaces.Insert(0, nds.Name.ToString()); + break; + case FileScopedNamespaceDeclarationSyntax file: + namespaces.Add(file.Name.ToString()); + isNamespaceFileScoped = true; + break; + default: + throw new InvalidOperationException($"Cannot handle {node}"); + } + + node = node.Parent; + } + + methodsToGenerate.Add(new(namespaces, isNamespaceFileScoped, classes, methodDeclarationSyntax.Identifier.ValueText, content)); + } } return methodsToGenerate; From 250d380873d2816ca4ba3270449ad987299bc709 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 15:32:26 -0500 Subject: [PATCH 09/26] Update to use differet types and IEnumerable when nothing has been selected --- src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs | 8 ++++---- ...ting.CombineTwoLists#.Class.CombineAsync.g.verified.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 4088155..5b80146 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -121,7 +121,7 @@ private static List GetTypesToGenerate(SourceProductionContext var methodName = methodSymbol.ToString(); - var variations = 0; + var variations = 8; foreach (AttributeData attributeData in methodSymbol.GetAttributes()) { if (!attribute.Equals(attributeData.AttributeClass, SymbolEqualityComparer.Default)) @@ -175,17 +175,17 @@ private static List GetTypesToGenerate(SourceProductionContext collections.Add("System.Collections.Generic.IList"); } - if (((CollectionTypes)variations & CollectionTypes.Span) == CollectionTypes.IList) + if (((CollectionTypes)variations & CollectionTypes.Span) == CollectionTypes.Span) { collections.Add("System.Span"); } - if (((CollectionTypes)variations & CollectionTypes.ReadOnlySpan) == CollectionTypes.IList) + if (((CollectionTypes)variations & CollectionTypes.ReadOnlySpan) == CollectionTypes.ReadOnlySpan) { collections.Add("System.ReadOnlySpan"); } - if (((CollectionTypes)variations & CollectionTypes.IEnumerable) == CollectionTypes.IList) + if (((CollectionTypes)variations & CollectionTypes.IEnumerable) == CollectionTypes.IEnumerable || collections.Count == 0) { collections.Add("System.Collections.Generic.IEnumerable"); } diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs @@ -0,0 +1 @@ + \ No newline at end of file From 7da552eff09a240ce85985b6e382c7382ee3c549 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Tue, 14 Nov 2023 16:37:22 -0500 Subject: [PATCH 10/26] Add tests for all variations of CollectionTypes --- tests/Generator.Tests/IntegrationTesting.cs | 17 +++++++++++++++++ ...TwoLists#.Class.CombineAsync.g.verified.cs | 19 ++++++++++++++++++- ...CombineTwoLists#CombineAsync.g.verified.cs | 2 +- ...ListsAll#.Class.CombineAsync.g.verified.cs | 18 ++++++++++++++++++ ...stsAll#.Class.CombineAsync_2.g.verified.cs | 18 ++++++++++++++++++ ...stsAll#.Class.CombineAsync_3.g.verified.cs | 18 ++++++++++++++++++ ...bineTwoListsAll#CombineAsync.g.verified.cs | 13 +++++++++++++ 7 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync.g.verified.cs create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_2.g.verified.cs create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_3.g.verified.cs create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#CombineAsync.g.verified.cs diff --git a/tests/Generator.Tests/IntegrationTesting.cs b/tests/Generator.Tests/IntegrationTesting.cs index 3ced161..f192cf8 100644 --- a/tests/Generator.Tests/IntegrationTesting.cs +++ b/tests/Generator.Tests/IntegrationTesting.cs @@ -69,6 +69,23 @@ public Task CombineTwoLists() => """ yield return (item, enumerator2.Current); } } +""".Verify(); + + [Fact] + public Task CombineTwoListsAll() => """ +[CreateSyncVersion(Variations = (CollectionTypes)15)] +public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) +{ + await using var enumerator2 = list2.GetAsyncEnumerator(ct); + await foreach (var item in list1.WithCancellation(ct).ConfigureAwait(false)) + { + if (!(await enumerator2.MoveNextAsync().ConfigureAwait(false))) + { + throw new InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } +} """.Verify(); #if NETCOREAPP1_0_OR_GREATER diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs index 5f28270..c7273b0 100644 --- a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#.Class.CombineAsync.g.verified.cs @@ -1 +1,18 @@ - \ No newline at end of file +//HintName: .Class.CombineAsync.g.cs +// +#nullable enable +partial class Class +{ + public static global::System.Span<(TLeft Left, TRight Right)> Combine(this global::System.Span list1, global::System.Span list2) + { + using var enumerator2 = list2.GetEnumerator(); + foreach (var item in list1) + { + if (!enumerator2.MoveNext()) + { + throw new global::System.InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } + } +} diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#CombineAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#CombineAsync.g.verified.cs index 73cb432..064a7ae 100644 --- a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#CombineAsync.g.verified.cs +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#CombineAsync.g.verified.cs @@ -1,5 +1,5 @@ //HintName: Test.Class.CombineAsync.g.cs -public static global::System.Collections.Generic.IEnumerable<(TLeft Left, TRight Right)> Combine(this global::System.Collections.Generic.IEnumerable list1, global::System.Collections.Generic.IEnumerable list2) +public static global::System.Collections.Generic.IList<(TLeft Left, TRight Right)> Combine(this global::System.Collections.Generic.IList list1, global::System.Collections.Generic.IList list2) { using var enumerator2 = list2.GetEnumerator(); foreach (var item in list1) diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync.g.verified.cs new file mode 100644 index 0000000..1b20b92 --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync.g.verified.cs @@ -0,0 +1,18 @@ +//HintName: .Class.CombineAsync.g.cs +// +#nullable enable +partial class Class +{ + public static global::System.Span<(TLeft Left, TRight Right)> Combine(this global::System.Span list1, global::System.Span list2) + { + using var enumerator2 = list2.GetEnumerator(); + foreach (var item in list1) + { + if (!enumerator2.MoveNext()) + { + throw new global::System.InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } + } +} diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_2.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_2.g.verified.cs new file mode 100644 index 0000000..e0315cf --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_2.g.verified.cs @@ -0,0 +1,18 @@ +//HintName: .Class.CombineAsync_2.g.cs +// +#nullable enable +partial class Class +{ + public static global::System.ReadOnlySpan<(TLeft Left, TRight Right)> Combine(this global::System.ReadOnlySpan list1, global::System.ReadOnlySpan list2) + { + using var enumerator2 = list2.GetEnumerator(); + foreach (var item in list1) + { + if (!enumerator2.MoveNext()) + { + throw new global::System.InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } + } +} diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_3.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_3.g.verified.cs new file mode 100644 index 0000000..01e02b2 --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#.Class.CombineAsync_3.g.verified.cs @@ -0,0 +1,18 @@ +//HintName: .Class.CombineAsync_3.g.cs +// +#nullable enable +partial class Class +{ + public static global::System.Collections.Generic.IEnumerable<(TLeft Left, TRight Right)> Combine(this global::System.Collections.Generic.IEnumerable list1, global::System.Collections.Generic.IEnumerable list2) + { + using var enumerator2 = list2.GetEnumerator(); + foreach (var item in list1) + { + if (!enumerator2.MoveNext()) + { + throw new global::System.InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } + } +} diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#CombineAsync.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#CombineAsync.g.verified.cs new file mode 100644 index 0000000..064a7ae --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoListsAll#CombineAsync.g.verified.cs @@ -0,0 +1,13 @@ +//HintName: Test.Class.CombineAsync.g.cs +public static global::System.Collections.Generic.IList<(TLeft Left, TRight Right)> Combine(this global::System.Collections.Generic.IList list1, global::System.Collections.Generic.IList list2) +{ + using var enumerator2 = list2.GetEnumerator(); + foreach (var item in list1) + { + if (!enumerator2.MoveNext()) + { + throw new global::System.InvalidOperationException("Must have the same size"); + } + yield return (item, enumerator2.Current); + } +} From ad229e32ccac2a52dab0d5888e2d90675c11874c Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 09:51:54 -0500 Subject: [PATCH 11/26] Move IEnumerable to front of enum and use its CollectionTypes values as default --- src/Zomp.SyncMethodGenerator/CollectionTypes.cs | 16 ++++++++-------- .../SourceGenerationHelper.cs | 11 +++++------ .../SyncMethodSourceGenerator.cs | 12 ++++++------ 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/CollectionTypes.cs b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs index 58be38c..b5673ad 100644 --- a/src/Zomp.SyncMethodGenerator/CollectionTypes.cs +++ b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs @@ -7,22 +7,22 @@ public enum CollectionTypes { /// - /// Type for System.Collections.Generic.IList . + /// Type for System.Collections.Generic.IEnumerable . /// - IList = 1, + IEnumerable = 1, /// - /// Type for System.ReadOnlySpan . + /// Type for System.Collections.Generic.IList . /// - ReadOnlySpan = 2, + IList = 2, /// - /// Type for System.Span . + /// Type for System.ReadOnlySpan . /// - Span = 4, + ReadOnlySpan = 4, /// - /// Type for System.Collections.Generic.IEnumerable . + /// Type for System.Span . /// - IEnumerable = 8, + Span = 8, } diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 89545f1..2fe0565 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -7,7 +7,6 @@ public static class SourceGenerationHelper { internal const string CreateSyncVersionAttributeSource = """ // -using System; namespace Zomp.SyncMethodGenerator { /// @@ -16,14 +15,14 @@ namespace Zomp.SyncMethodGenerator [Flags] public enum CollectionTypes { + /// IEnumerable + IEnumerable = 1, /// IList - IList = 1, + IList = 2, /// ReadOnlySpan - ReadOnlySpan = 2, + ReadOnlySpan = 4, /// Span - Span = 4, - /// IEnumerable - IEnumerable = 8 + Span = 8, } /// diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 5b80146..bff4b74 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -121,7 +121,7 @@ private static List GetTypesToGenerate(SourceProductionContext var methodName = methodSymbol.ToString(); - var variations = 8; + var variations = CollectionTypes.IEnumerable; foreach (AttributeData attributeData in methodSymbol.GetAttributes()) { if (!attribute.Equals(attributeData.AttributeClass, SymbolEqualityComparer.Default)) @@ -129,7 +129,7 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is int value) + if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is CollectionTypes value) { variations = value; } @@ -170,22 +170,22 @@ private static List GetTypesToGenerate(SourceProductionContext var collections = new List(); - if (((CollectionTypes)variations & CollectionTypes.IList) == CollectionTypes.IList) + if ((variations & CollectionTypes.IList) == CollectionTypes.IList) { collections.Add("System.Collections.Generic.IList"); } - if (((CollectionTypes)variations & CollectionTypes.Span) == CollectionTypes.Span) + if ((variations & CollectionTypes.Span) == CollectionTypes.Span) { collections.Add("System.Span"); } - if (((CollectionTypes)variations & CollectionTypes.ReadOnlySpan) == CollectionTypes.ReadOnlySpan) + if ((variations & CollectionTypes.ReadOnlySpan) == CollectionTypes.ReadOnlySpan) { collections.Add("System.ReadOnlySpan"); } - if (((CollectionTypes)variations & CollectionTypes.IEnumerable) == CollectionTypes.IEnumerable || collections.Count == 0) + if ((variations & CollectionTypes.IEnumerable) == CollectionTypes.IEnumerable || collections.Count == 0) { collections.Add("System.Collections.Generic.IEnumerable"); } From aee4d45d4924462811e56182df4b0bbea9abdfba Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 09:59:13 -0500 Subject: [PATCH 12/26] Add using system directive back in --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 2fe0565..1f6e5a6 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -7,6 +7,7 @@ public static class SourceGenerationHelper { internal const string CreateSyncVersionAttributeSource = """ // +using System; namespace Zomp.SyncMethodGenerator { /// From 646522bb81c0ec8213a0956879fd8b8611ce76b6 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 12:26:32 -0500 Subject: [PATCH 13/26] Fix test failures, and alter test to use CollectionTypes. --- src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs | 4 ++-- tests/Generator.Tests/IntegrationTesting.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index bff4b74..122c172 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -129,9 +129,9 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is CollectionTypes value) + if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is int value) { - variations = value; + variations = (CollectionTypes)value; } break; diff --git a/tests/Generator.Tests/IntegrationTesting.cs b/tests/Generator.Tests/IntegrationTesting.cs index f192cf8..39be355 100644 --- a/tests/Generator.Tests/IntegrationTesting.cs +++ b/tests/Generator.Tests/IntegrationTesting.cs @@ -56,7 +56,7 @@ async Task EnumeratorTestAsync(IAsyncEnumerable range, CancellationToken ct [Fact] public Task CombineTwoLists() => """ -[CreateSyncVersion(Variations = (CollectionTypes)5)] +[CreateSyncVersion(Variations = (CollectionTypes.IList | CollectionTypes.Span))] public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) { await using var enumerator2 = list2.GetAsyncEnumerator(ct); @@ -73,7 +73,7 @@ public Task CombineTwoLists() => """ [Fact] public Task CombineTwoListsAll() => """ -[CreateSyncVersion(Variations = (CollectionTypes)15)] +[CreateSyncVersion(Variations = (CollectionTypes.IList | CollectionTypes.Span | CollectionTypes.ReadOnlySpan | CollectionTypes.IEnumerable))] public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) { await using var enumerator2 = list2.GetAsyncEnumerator(ct); From da595e6bfeb4e3c50a4a076209567a3c4ce25d48 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 14:30:31 -0500 Subject: [PATCH 14/26] Remove private property --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 1f6e5a6..1715158 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -32,13 +32,7 @@ public enum CollectionTypes [System.AttributeUsage(System.AttributeTargets.Method)] internal class CreateSyncVersionAttribute : System.Attribute { - private CollectionTypes variations; - - public virtual CollectionTypes Variations - { - get { return variations; } - set { variations = value; } - } + public virtual CollectionTypes Variations { get; set; } } } """; From c8744a089eaa537b8ec7957146d9c8d3ff6340f1 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 15:26:27 -0500 Subject: [PATCH 15/26] Match by parameter name instead of list index --- src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 122c172..fb8720f 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -129,7 +129,7 @@ private static List GetTypesToGenerate(SourceProductionContext continue; } - if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments[0].Value.Value is int value) + if (attributeData.NamedArguments.Length >= 1 && attributeData.NamedArguments.FirstOrDefault(c => c.Key == "Variations").Value.Value is int value) { variations = (CollectionTypes)value; } From f6cff4588702ee58bec9597872b6a14406376db6 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Wed, 15 Nov 2023 17:19:52 -0500 Subject: [PATCH 16/26] Add attribute for options --- src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 1715158..5498888 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -34,6 +34,15 @@ internal class CreateSyncVersionAttribute : System.Attribute { public virtual CollectionTypes Variations { get; set; } } + + /// + /// An attribute that can be used to specify the synchronous type a parameter should be converted into . + /// + [System.AttributeUsage(System.AttributeTargets.Parameter)] + internal class ReplaceWithAttribute : System.Attribute + { + public virtual CollectionTypes Variations { get; set; } + } } """; From 1916f7ca97a96a1ee083ba755d1bed39f708b2e0 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 10:03:18 -0500 Subject: [PATCH 17/26] Seperate the ReplaceWith Attribute --- .../SourceGenerationHelper.cs | 15 +++++++++++++++ .../SyncMethodSourceGenerator.cs | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index 5498888..edbfadb 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -44,6 +44,21 @@ internal class ReplaceWithAttribute : System.Attribute public virtual CollectionTypes Variations { get; set; } } } +"""; + + internal const string ReplaceWithAttributeSource = """ +// +namespace Zomp.SyncMethodGenerator +{ + /// + /// An attribute that can be used to specify the synchronous type a parameter should be converted into . + /// + [System.AttributeUsage(System.AttributeTargets.Parameter)] + internal class ReplaceWithAttribute : System.Attribute + { + public virtual CollectionTypes Variations { get; set; } + } +} """; internal static string GenerateExtensionClass(MethodToGenerate methodToGenerate) diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index fb8720f..d750d1b 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -10,6 +10,11 @@ public class SyncMethodSourceGenerator : IIncrementalGenerator /// Create sync version attribute string. /// public const string CreateSyncVersionAttribute = "CreateSyncVersionAttribute"; + + /// + /// Replace with attribute string. + /// + public const string ReplaceWithAttribute = "ReplaceWithAttribute"; internal const string QualifiedCreateSyncVersionAttribute = $"{ThisAssembly.RootNamespace}.{CreateSyncVersionAttribute}"; /// @@ -25,6 +30,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterPostInitializationOutput(ctx => ctx.AddSource( $"{CreateSyncVersionAttribute}.g.cs", SourceText.From(SourceGenerationHelper.CreateSyncVersionAttributeSource, Encoding.UTF8))); + context.RegisterPostInitializationOutput(ctx => ctx.AddSource( + $"{ReplaceWithAttribute}.g.cs", SourceText.From(SourceGenerationHelper.ReplaceWithAttributeSource, Encoding.UTF8))); + IncrementalValuesProvider methodDeclarations = context.SyntaxProvider .ForAttributeWithMetadataName( QualifiedCreateSyncVersionAttribute, From c12fb15ebc9e31bad7f9e25d93b615b4196cf9c7 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 11:10:50 -0500 Subject: [PATCH 18/26] Remove attribute from generated function --- src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs | 5 ++++- .../SyncMethodSourceGenerator.cs | 1 + tests/Generator.Tests/IntegrationTesting.cs | 2 +- ...mbineTwoLists#ReplaceWithAttribute.g.verified.cs | 13 +++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#ReplaceWithAttribute.g.verified.cs diff --git a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs index cc5e3d9..85e6217 100644 --- a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs +++ b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs @@ -870,7 +870,7 @@ bool ShouldRemoveAttribute(AttributeSyntax attributeSyntax) var attributeContainingTypeSymbol = attributeSymbol.ContainingType; // Is the attribute [CreateSyncVersion] attribute? - return IsCreateSyncVersionAttribute(attributeContainingTypeSymbol); + return IsCreateSyncVersionAttribute(attributeContainingTypeSymbol) || IsReplaceWithAttribute(attributeContainingTypeSymbol); } var @base = (AttributeListSyntax)base.VisitAttributeList(node)!; @@ -1016,6 +1016,9 @@ private static bool CanDropElse(ElseClauseSyntax @else) private static bool IsCreateSyncVersionAttribute(INamedTypeSymbol s) => s.ToDisplayString() == SyncMethodSourceGenerator.QualifiedCreateSyncVersionAttribute; + private static bool IsReplaceWithAttribute(INamedTypeSymbol s) + => s.ToDisplayString() == SyncMethodSourceGenerator.QualifiedReplaceWithAttribute; + private static SyntaxList RemoveAtRange(SyntaxList list, IEnumerable indices) where TNode : SyntaxNode { diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index d750d1b..5ef4578 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -16,6 +16,7 @@ public class SyncMethodSourceGenerator : IIncrementalGenerator /// public const string ReplaceWithAttribute = "ReplaceWithAttribute"; internal const string QualifiedCreateSyncVersionAttribute = $"{ThisAssembly.RootNamespace}.{CreateSyncVersionAttribute}"; + internal const string QualifiedReplaceWithAttribute = $"{ThisAssembly.RootNamespace}.{ReplaceWithAttribute}"; /// public void Initialize(IncrementalGeneratorInitializationContext context) diff --git a/tests/Generator.Tests/IntegrationTesting.cs b/tests/Generator.Tests/IntegrationTesting.cs index 39be355..f652ea4 100644 --- a/tests/Generator.Tests/IntegrationTesting.cs +++ b/tests/Generator.Tests/IntegrationTesting.cs @@ -57,7 +57,7 @@ async Task EnumeratorTestAsync(IAsyncEnumerable range, CancellationToken ct [Fact] public Task CombineTwoLists() => """ [CreateSyncVersion(Variations = (CollectionTypes.IList | CollectionTypes.Span))] -public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) +public static async IAsyncEnumerable<(TLeft Left, TRight Right)> CombineAsync(this IAsyncEnumerable list1, [ReplaceWith(Variations = CollectionTypes.Span)]IAsyncEnumerable list2, [EnumeratorCancellation] CancellationToken ct = default) { await using var enumerator2 = list2.GetAsyncEnumerator(ct); await foreach (var item in list1.WithCancellation(ct).ConfigureAwait(false)) diff --git a/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#ReplaceWithAttribute.g.verified.cs b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#ReplaceWithAttribute.g.verified.cs new file mode 100644 index 0000000..f8bb4bf --- /dev/null +++ b/tests/Generator.Tests/Snapshots/IntegrationTesting.CombineTwoLists#ReplaceWithAttribute.g.verified.cs @@ -0,0 +1,13 @@ +//HintName: ReplaceWithAttribute.g.cs +// +namespace Zomp.SyncMethodGenerator +{ + /// + /// An attribute that can be used to specify the synchronous type a parameter should be converted into . + /// + [System.AttributeUsage(System.AttributeTargets.Parameter)] + internal class ReplaceWithAttribute : System.Attribute + { + public virtual CollectionTypes Variations { get; set; } + } +} From 09bd558f12b4c5a26fa05869f716959936dddadf Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 12:53:41 -0500 Subject: [PATCH 19/26] Add new paramter types to dictionary from replace attribute --- .../AsyncToSyncRewriter.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs index 85e6217..b87af23 100644 --- a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs +++ b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs @@ -59,6 +59,7 @@ internal sealed class AsyncToSyncRewriter(SemanticModel semanticModel, Dictionar private readonly HashSet memoryToSpan = []; private readonly Dictionary renamedLocalFunctions = []; private readonly ImmutableArray.Builder diagnostics = ImmutableArray.CreateBuilder(); + private Dictionary parametersWithTypes = []; private enum SyncOnlyDirectiveType { @@ -215,6 +216,9 @@ static BinaryExpressionSyntax CheckNull(ExpressionSyntax expr) => BinaryExpressi string? newType = null; + //if (@base.Att) + // { + // } if (replacementOverrides.TryGetValue(genericName, out var replacement) || Replacements.TryGetValue(genericName, out replacement)) { @@ -874,6 +878,7 @@ bool ShouldRemoveAttribute(AttributeSyntax attributeSyntax) } var @base = (AttributeListSyntax)base.VisitAttributeList(node)!; + node.Attributes.ToList().ForEach(a => AddParameterTypes(a)); var indices = node.Attributes.GetIndices((a, _) => ShouldRemoveAttribute(a)); var newList = RemoveAtRange(@base.Attributes, indices); return @base.WithAttributes(newList); @@ -881,6 +886,7 @@ bool ShouldRemoveAttribute(AttributeSyntax attributeSyntax) public override SyntaxNode? VisitAttribute(AttributeSyntax node) { + parametersWithTypes.ContainsKey("k"); var @base = (AttributeSyntax)base.VisitAttribute(node)!; if (GetSymbol(node.Name) is not IMethodSymbol ms) @@ -1194,6 +1200,18 @@ private static InvocationExpressionSyntax UnwrapExtension(InvocationExpressionSy return replacement; } + private void AddParameterTypes(AttributeSyntax attributeSyntax) + { + if (GetSymbol(attributeSyntax) is IMethodSymbol attributeSymbol) + { + var attributeContainingTypeSymbol = attributeSymbol.ContainingType; + if (IsReplaceWithAttribute(attributeContainingTypeSymbol) && attributeSyntax.Parent?.Parent is ParameterSyntax param) + { + parametersWithTypes.Add(param.Identifier.ValueText, param.Identifier.Text); + } + } + } + private bool PreProcess( SyntaxList statements, Dictionary extraNodeInfoList, From d63db80a5216b4e96d05d3ae6c69b950b2e12569 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 14:48:31 -0500 Subject: [PATCH 20/26] Remove attribute from duplicating --- .../SourceGenerationHelper.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index edbfadb..c56259f 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -7,13 +7,12 @@ public static class SourceGenerationHelper { internal const string CreateSyncVersionAttributeSource = """ // -using System; namespace Zomp.SyncMethodGenerator { /// /// All types that an IAsyncEnumerable can be converted into /// - [Flags] + [global::System.Flags] public enum CollectionTypes { /// IEnumerable @@ -34,15 +33,6 @@ internal class CreateSyncVersionAttribute : System.Attribute { public virtual CollectionTypes Variations { get; set; } } - - /// - /// An attribute that can be used to specify the synchronous type a parameter should be converted into . - /// - [System.AttributeUsage(System.AttributeTargets.Parameter)] - internal class ReplaceWithAttribute : System.Attribute - { - public virtual CollectionTypes Variations { get; set; } - } } """; From 06d4e75e72faa8e722dba117a7504a95e8b38c46 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 15:31:47 -0500 Subject: [PATCH 21/26] Stop tests from comparing with new generated attribute --- tests/Generator.Tests/TestHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Generator.Tests/TestHelper.cs b/tests/Generator.Tests/TestHelper.cs index 63cddf7..1b67e90 100644 --- a/tests/Generator.Tests/TestHelper.cs +++ b/tests/Generator.Tests/TestHelper.cs @@ -132,7 +132,7 @@ partial class Class var target = new RunResultWithIgnoreList { Result = driver.GetRunResult(), - IgnoredFiles = { $"{SyncMethodSourceGenerator.CreateSyncVersionAttribute}.g.cs" }, + IgnoredFiles = { $"{SyncMethodSourceGenerator.CreateSyncVersionAttribute}.g.cs", $"{SyncMethodSourceGenerator.ReplaceWithAttribute}.g.cs" }, }; var verifier = Verifier From 9d5b6388f58e3dfa5e6a489288d7253c69f92735 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 17:23:55 -0500 Subject: [PATCH 22/26] Retireve attribute and find its variation --- src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs index b87af23..5eab43a 100644 --- a/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs +++ b/src/Zomp.SyncMethodGenerator/AsyncToSyncRewriter.cs @@ -1205,9 +1205,13 @@ private void AddParameterTypes(AttributeSyntax attributeSyntax) if (GetSymbol(attributeSyntax) is IMethodSymbol attributeSymbol) { var attributeContainingTypeSymbol = attributeSymbol.ContainingType; - if (IsReplaceWithAttribute(attributeContainingTypeSymbol) && attributeSyntax.Parent?.Parent is ParameterSyntax param) + if (IsReplaceWithAttribute(attributeContainingTypeSymbol) && attributeSyntax.Parent?.Parent is ParameterSyntax param + && GetSymbol(param) is IParameterSymbol parameterSymbol) { - parametersWithTypes.Add(param.Identifier.ValueText, param.Identifier.Text); + var variation = parameterSymbol.GetAttributes()[0].NamedArguments.FirstOrDefault(c => c.Key == "Variations").Value.Value; + + //.NamedArguments.FirstOrDefault(c => c.Key == "Variations").Value.Value; + parametersWithTypes.Add(param.Identifier.ValueText, param.Identifier.ValueText); } } } From c4aa8b6adb4677bb10e5eccf253462567a7cebd8 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Thu, 16 Nov 2023 17:28:32 -0500 Subject: [PATCH 23/26] Add CollectionTypes enum as embedded resource (not working as of now) --- tests/Generator.Tests/Generator.Tests.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/Generator.Tests/Generator.Tests.csproj b/tests/Generator.Tests/Generator.Tests.csproj index 407aec1..9fe2e61 100644 --- a/tests/Generator.Tests/Generator.Tests.csproj +++ b/tests/Generator.Tests/Generator.Tests.csproj @@ -32,6 +32,11 @@ + + + Zomp.SyncMethodGenerator.CollectionTypes.cs + + From 1d94cb8a8f765cfafccf494c5b7e1c947087682a Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Fri, 17 Nov 2023 10:49:26 -0500 Subject: [PATCH 24/26] Fix references in test projects --- .../SourceGenerationHelper.cs | 16 ---------------- .../GenerationSandbox.Tests.csproj | 5 +++++ tests/Generator.Tests/Generator.Tests.csproj | 4 ++-- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs index c56259f..5289ded 100644 --- a/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs +++ b/src/Zomp.SyncMethodGenerator/SourceGenerationHelper.cs @@ -9,22 +9,6 @@ public static class SourceGenerationHelper // namespace Zomp.SyncMethodGenerator { - /// - /// All types that an IAsyncEnumerable can be converted into - /// - [global::System.Flags] - public enum CollectionTypes - { - /// IEnumerable - IEnumerable = 1, - /// IList - IList = 2, - /// ReadOnlySpan - ReadOnlySpan = 4, - /// Span - Span = 8, - } - /// /// An attribute that can be used to automatically generate a synchronous version of an async method. Must be used in a partial class. /// diff --git a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj index d55f488..5a1029e 100644 --- a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj +++ b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj @@ -27,6 +27,11 @@ + + + Zomp.SyncMethodGenerator.tests.CollectionTypes.cs + + diff --git a/tests/Generator.Tests/Generator.Tests.csproj b/tests/Generator.Tests/Generator.Tests.csproj index 9fe2e61..c24854e 100644 --- a/tests/Generator.Tests/Generator.Tests.csproj +++ b/tests/Generator.Tests/Generator.Tests.csproj @@ -33,8 +33,8 @@ - - Zomp.SyncMethodGenerator.CollectionTypes.cs + + Zomp.SyncMethodGenerator.tests.CollectionTypes.cs From 8a6b1c9f86b51dd6f0c1079107d4d097ff91ae63 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Fri, 17 Nov 2023 14:52:05 -0500 Subject: [PATCH 25/26] Fix logical path --- tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj | 1 - tests/Generator.Tests/Generator.Tests.csproj | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj index 5a1029e..28c0fd9 100644 --- a/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj +++ b/tests/GenerationSandbox.Tests/GenerationSandbox.Tests.csproj @@ -29,7 +29,6 @@ - Zomp.SyncMethodGenerator.tests.CollectionTypes.cs diff --git a/tests/Generator.Tests/Generator.Tests.csproj b/tests/Generator.Tests/Generator.Tests.csproj index c24854e..693626d 100644 --- a/tests/Generator.Tests/Generator.Tests.csproj +++ b/tests/Generator.Tests/Generator.Tests.csproj @@ -34,7 +34,7 @@ - Zomp.SyncMethodGenerator.tests.CollectionTypes.cs + Generator.Tests.CollectionTypes.cs From f3e93588831e84b31b2a5fbd1ad50ad5a1fa5dc1 Mon Sep 17 00:00:00 2001 From: ValerieFernandes Date: Fri, 17 Nov 2023 17:46:10 -0500 Subject: [PATCH 26/26] Generate enum from file instead of string --- .../CollectionTypes.cs | 43 ++++++++++--------- .../SyncMethodSourceGenerator.cs | 14 ++++++ .../Zomp.SyncMethodGenerator.csproj | 6 +++ tests/Generator.Tests/TestHelper.cs | 2 +- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/Zomp.SyncMethodGenerator/CollectionTypes.cs b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs index b5673ad..e50dee6 100644 --- a/src/Zomp.SyncMethodGenerator/CollectionTypes.cs +++ b/src/Zomp.SyncMethodGenerator/CollectionTypes.cs @@ -1,28 +1,29 @@ -namespace Zomp.SyncMethodGenerator; - -/// -/// All types that an IAsyncEnumerable can be converted into. -/// -[Flags] -public enum CollectionTypes +namespace Zomp.SyncMethodGenerator { /// - /// Type for System.Collections.Generic.IEnumerable . + /// All types that an IAsyncEnumerable can be converted into. /// - IEnumerable = 1, + [Flags] + public enum CollectionTypes + { + /// + /// Type for System.Collections.Generic.IEnumerable . + /// + IEnumerable = 1, - /// - /// Type for System.Collections.Generic.IList . - /// - IList = 2, + /// + /// Type for System.Collections.Generic.IList . + /// + IList = 2, - /// - /// Type for System.ReadOnlySpan . - /// - ReadOnlySpan = 4, + /// + /// Type for System.ReadOnlySpan . + /// + ReadOnlySpan = 4, - /// - /// Type for System.Span . - /// - Span = 8, + /// + /// Type for System.Span . + /// + Span = 8, + } } diff --git a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs index 5ef4578..7954b85 100644 --- a/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs +++ b/src/Zomp.SyncMethodGenerator/SyncMethodSourceGenerator.cs @@ -28,6 +28,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) Debugger.Launch(); } #endif + + context.RegisterPostInitializationOutput(ctx => ctx.AddSource( + $"CollectionTypes.g.cs", SourceText.From(GetResourceText("Zomp.SyncMethodGenerator.tests.CollectionTypes.cs"), Encoding.UTF8))); + context.RegisterPostInitializationOutput(ctx => ctx.AddSource( $"{CreateSyncVersionAttribute}.g.cs", SourceText.From(SourceGenerationHelper.CreateSyncVersionAttributeSource, Encoding.UTF8))); @@ -249,4 +253,14 @@ private static List GetTypesToGenerate(SourceProductionContext return methodsToGenerate; } + + private string GetResourceText(string name) + { + using var stream = GetType().Assembly.GetManifestResourceStream(name); + using var streamReader = new StreamReader(stream); + return $""" + // + {streamReader.ReadToEnd()} + """; + } } diff --git a/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj b/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj index a7c2bbc..19b4cac 100644 --- a/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj +++ b/src/Zomp.SyncMethodGenerator/Zomp.SyncMethodGenerator.csproj @@ -24,6 +24,12 @@ + + + + Zomp.SyncMethodGenerator.tests.CollectionTypes.cs + + diff --git a/tests/Generator.Tests/TestHelper.cs b/tests/Generator.Tests/TestHelper.cs index 1b67e90..db274e9 100644 --- a/tests/Generator.Tests/TestHelper.cs +++ b/tests/Generator.Tests/TestHelper.cs @@ -132,7 +132,7 @@ partial class Class var target = new RunResultWithIgnoreList { Result = driver.GetRunResult(), - IgnoredFiles = { $"{SyncMethodSourceGenerator.CreateSyncVersionAttribute}.g.cs", $"{SyncMethodSourceGenerator.ReplaceWithAttribute}.g.cs" }, + IgnoredFiles = { $"{SyncMethodSourceGenerator.CreateSyncVersionAttribute}.g.cs", $"{SyncMethodSourceGenerator.ReplaceWithAttribute}.g.cs", $"CollectionTypes.g.cs" }, }; var verifier = Verifier