Skip to content

Commit 5a1ea0a

Browse files
authored
Implement ToImmutable*Async extension methods (#1545)
1 parent 1ff45fb commit 5a1ea0a

File tree

19 files changed

+1169
-49
lines changed

19 files changed

+1169
-49
lines changed

Ix.NET/Source/System.Linq.Async.SourceGenerator/AsyncOverloadsGenerator.cs

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,21 @@ private static IEnumerable<AsyncMethodGrouping> GetMethodsGroupedBySyntaxTree(Ge
5252

5353
private static string GenerateOverloads(AsyncMethodGrouping grouping, GenerationOptions options)
5454
{
55-
var usings = grouping.SyntaxTree.GetRoot() is CompilationUnitSyntax compilationUnit
56-
? compilationUnit.Usings.ToString()
57-
: string.Empty;
58-
59-
var overloads = new StringBuilder();
60-
overloads.AppendLine("#nullable enable");
61-
overloads.AppendLine(usings);
62-
overloads.AppendLine("namespace System.Linq");
63-
overloads.AppendLine("{");
64-
overloads.AppendLine(" partial class AsyncEnumerable");
65-
overloads.AppendLine(" {");
66-
67-
foreach (var method in grouping.Methods)
68-
overloads.AppendLine(GenerateOverload(method, options));
69-
70-
overloads.AppendLine(" }");
71-
overloads.AppendLine("}");
72-
73-
return overloads.ToString();
55+
var compilationRoot = grouping.SyntaxTree.GetCompilationUnitRoot();
56+
var namespaceDeclaration = compilationRoot.ChildNodes().OfType<NamespaceDeclarationSyntax>().Single();
57+
var classDeclaration = namespaceDeclaration.ChildNodes().OfType<ClassDeclarationSyntax>().Single();
58+
59+
return CompilationUnit()
60+
.WithUsings(List(compilationRoot.Usings.Select(@using => @using.WithoutTrivia())))
61+
.AddMembers(NamespaceDeclaration(namespaceDeclaration.Name)
62+
.AddMembers(ClassDeclaration(classDeclaration.Identifier)
63+
.AddModifiers(Token(SyntaxKind.PartialKeyword))
64+
.WithMembers(List(grouping.Methods.Select(method => GenerateOverload(method, options))))))
65+
.NormalizeWhitespace()
66+
.ToFullString();
7467
}
7568

76-
private static string GenerateOverload(AsyncMethod method, GenerationOptions options)
69+
private static MemberDeclarationSyntax GenerateOverload(AsyncMethod method, GenerationOptions options)
7770
=> MethodDeclaration(method.Syntax.ReturnType, GetMethodName(method.Symbol, options))
7871
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
7972
.WithTypeParameterList(method.Syntax.TypeParameterList)
@@ -87,9 +80,7 @@ private static string GenerateOverload(AsyncMethod method, GenerationOptions opt
8780
method.Syntax.ParameterList.Parameters
8881
.Select(p => Argument(IdentifierName(p.Identifier))))))))
8982
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
90-
.WithLeadingTrivia(method.Syntax.GetLeadingTrivia().Where(t => t.GetStructure() is not DirectiveTriviaSyntax))
91-
.NormalizeWhitespace()
92-
.ToFullString();
83+
.WithLeadingTrivia(method.Syntax.GetLeadingTrivia().Where(t => t.GetStructure() is not DirectiveTriviaSyntax));
9384

9485
private static INamedTypeSymbol GetAsyncOverloadAttributeSymbol(GeneratorExecutionContext context)
9586
=> context.Compilation.GetTypeByMetadataName("System.Linq.GenerateAsyncOverloadAttribute") ?? throw new InvalidOperationException();
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Xunit;
12+
13+
namespace Tests
14+
{
15+
public class ToImmutableArray : AsyncEnumerableTests
16+
{
17+
[Fact]
18+
public async Task ToImmutableArray_Null()
19+
{
20+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableArrayAsyncEnumerableExtensions.ToImmutableArrayAsync<int>(default).AsTask());
21+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableArrayAsyncEnumerableExtensions.ToImmutableArrayAsync<int>(default, CancellationToken.None).AsTask());
22+
}
23+
24+
[Fact]
25+
public async Task ToImmutableArray_IAsyncIListProvider_Simple()
26+
{
27+
var xs = new[] { 42, 25, 39 };
28+
var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync();
29+
Assert.True((await res).SequenceEqual(xs));
30+
}
31+
32+
[Fact]
33+
public async Task ToImmutableArray_IAsyncIListProvider_Empty1()
34+
{
35+
var xs = new int[0];
36+
var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync();
37+
Assert.True((await res).SequenceEqual(xs));
38+
}
39+
40+
[Fact]
41+
public async Task ToImmutableArray_IAsyncIListProvider_Empty2()
42+
{
43+
var xs = new HashSet<int>();
44+
var res = xs.ToAsyncEnumerable().ToImmutableArrayAsync();
45+
Assert.True((await res).SequenceEqual(xs));
46+
}
47+
48+
[Fact]
49+
public async Task ToImmutableArray_Empty()
50+
{
51+
var xs = AsyncEnumerable.Empty<int>();
52+
var res = xs.ToImmutableArrayAsync();
53+
Assert.True((await res).Length == 0);
54+
}
55+
56+
[Fact]
57+
public async Task ToImmutableArray_Throw()
58+
{
59+
var ex = new Exception("Bang!");
60+
var res = Throw<int>(ex).ToImmutableArrayAsync();
61+
await AssertThrowsAsync(res, ex);
62+
}
63+
64+
[Fact]
65+
public async Task ToImmutableArray_Query()
66+
{
67+
var xs = await AsyncEnumerable.Range(5, 50).Take(10).ToImmutableArrayAsync();
68+
var ex = new[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
69+
70+
Assert.True(ex.SequenceEqual(xs));
71+
}
72+
73+
[Fact]
74+
public async Task ToImmutableArray_Set()
75+
{
76+
var res = new[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
77+
var xs = new HashSet<int>(res);
78+
79+
var arr = await xs.ToAsyncEnumerable().ToImmutableArrayAsync();
80+
81+
Assert.True(res.SequenceEqual(arr));
82+
}
83+
}
84+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Xunit;
12+
13+
namespace Tests
14+
{
15+
public class ToImmutableDictionary : AsyncEnumerableTests
16+
{
17+
[Fact]
18+
public async Task ToImmutableDictionary_Null()
19+
{
20+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int>(default, x => 0).AsTask());
21+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default(Func<int, int>)).AsTask());
22+
23+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int>(default, x => 0, EqualityComparer<int>.Default).AsTask());
24+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, EqualityComparer<int>.Default).AsTask());
25+
26+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(default, x => 0, x => 0).AsTask());
27+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, default, x => 0).AsTask());
28+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, x => 0, default).AsTask());
29+
30+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(default, x => 0, x => 0, EqualityComparer<int>.Default).AsTask());
31+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0, EqualityComparer<int>.Default).AsTask());
32+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, x => 0, default, EqualityComparer<int>.Default).AsTask());
33+
34+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int>(default, x => 0, CancellationToken.None).AsTask());
35+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default(Func<int, int>), CancellationToken.None).AsTask());
36+
37+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int>(default, x => 0, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
38+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
39+
40+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(default, x => 0, x => 0, CancellationToken.None).AsTask());
41+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, default, x => 0, CancellationToken.None).AsTask());
42+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, x => 0, default, CancellationToken.None).AsTask());
43+
44+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(default, x => 0, x => 0, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
45+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync(Return42, default, x => 0, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
46+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableDictionaryAsyncEnumerableExtensions.ToImmutableDictionaryAsync<int, int, int>(Return42, x => 0, default, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
47+
}
48+
49+
[Fact]
50+
public async Task ToImmutableDictionary1Async()
51+
{
52+
var xs = new[] { 1, 4 }.ToAsyncEnumerable();
53+
var res = await xs.ToImmutableDictionaryAsync(x => x % 2);
54+
Assert.True(res[0] == 4);
55+
Assert.True(res[1] == 1);
56+
}
57+
58+
[Fact]
59+
public async Task ToImmutableDictionary2Async()
60+
{
61+
var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable();
62+
await AssertThrowsAsync<ArgumentException>(xs.ToImmutableDictionaryAsync(x => x % 2).AsTask());
63+
}
64+
65+
[Fact]
66+
public async Task ToImmutableDictionary3Async()
67+
{
68+
var xs = new[] { 1, 4 }.ToAsyncEnumerable();
69+
var res = await xs.ToImmutableDictionaryAsync(x => x % 2, x => x + 1);
70+
Assert.True(res[0] == 5);
71+
Assert.True(res[1] == 2);
72+
}
73+
74+
[Fact]
75+
public async Task ToImmutableDictionary4Async()
76+
{
77+
var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable();
78+
await AssertThrowsAsync<ArgumentException>(xs.ToImmutableDictionaryAsync(x => x % 2, x => x + 1).AsTask());
79+
}
80+
81+
[Fact]
82+
public async Task ToImmutableDictionary5Async()
83+
{
84+
var xs = new[] { 1, 4 }.ToAsyncEnumerable();
85+
var res = await xs.ToImmutableDictionaryAsync(x => x % 2, new Eq());
86+
Assert.True(res[0] == 4);
87+
Assert.True(res[1] == 1);
88+
}
89+
90+
[Fact]
91+
public async Task ToImmutableDictionary6Async()
92+
{
93+
var xs = new[] { 1, 4, 2 }.ToAsyncEnumerable();
94+
await AssertThrowsAsync<ArgumentException>(xs.ToImmutableDictionaryAsync(x => x % 2, new Eq()).AsTask());
95+
}
96+
97+
[Fact]
98+
public async Task ToImmutableDictionary7Async()
99+
{
100+
var xs = new[] { 1, 4 }.ToAsyncEnumerable();
101+
var res = await xs.ToImmutableDictionaryAsync(x => x % 2, x => x, new Eq());
102+
Assert.True(res[0] == 4);
103+
Assert.True(res[1] == 1);
104+
}
105+
106+
private sealed class Eq : IEqualityComparer<int>
107+
{
108+
public bool Equals(int x, int y) => EqualityComparer<int>.Default.Equals(Math.Abs(x), Math.Abs(y));
109+
110+
public int GetHashCode(int obj) => EqualityComparer<int>.Default.GetHashCode(Math.Abs(obj));
111+
}
112+
}
113+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Xunit;
12+
13+
namespace Tests
14+
{
15+
public class ToImmutableHashSet : AsyncEnumerableTests
16+
{
17+
[Fact]
18+
public async Task ToImmutableHashSet_Null()
19+
{
20+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync<int>(default).AsTask());
21+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync<int>(default, CancellationToken.None).AsTask());
22+
23+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableHashSetAsyncEnumerableExtensions.ToImmutableHashSetAsync(default, EqualityComparer<int>.Default, CancellationToken.None).AsTask());
24+
}
25+
26+
[Fact]
27+
public async Task ToImmutableHashSet_Simple()
28+
{
29+
var xs = new[] { 1, 2, 1, 2, 3, 4, 1, 2, 3, 4 };
30+
var res = xs.ToAsyncEnumerable().ToImmutableHashSetAsync();
31+
Assert.True((await res).OrderBy(x => x).SequenceEqual(new[] { 1, 2, 3, 4 }));
32+
}
33+
34+
[Fact]
35+
public async Task ToImmutableHashSet_Comparer()
36+
{
37+
var xs = new[] { 1, 12, 11, 2, 3, 14, 1, 12, 13, 4 };
38+
var res = xs.ToAsyncEnumerable().ToImmutableHashSetAsync(new Eq());
39+
Assert.True((await res).OrderBy(x => x).SequenceEqual(new[] { 1, 3, 12, 14 }));
40+
}
41+
42+
private class Eq : IEqualityComparer<int>
43+
{
44+
public bool Equals(int x, int y) => x % 10 == y % 10;
45+
46+
public int GetHashCode(int obj) => obj % 10;
47+
}
48+
}
49+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Immutable;
7+
using System.Linq;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using Xunit;
11+
12+
namespace Tests
13+
{
14+
public class ToImmutableList : AsyncEnumerableTests
15+
{
16+
[Fact]
17+
public async Task ToImmutableList_Null()
18+
{
19+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableListAsyncEnumerableExtensions.ToImmutableListAsync<int>(default).AsTask());
20+
await Assert.ThrowsAsync<ArgumentNullException>(() => ImmutableListAsyncEnumerableExtensions.ToImmutableListAsync<int>(default, CancellationToken.None).AsTask());
21+
}
22+
23+
[Fact]
24+
public async Task ToImmutableList_Simple()
25+
{
26+
var xs = new[] { 42, 25, 39 };
27+
var res = xs.ToAsyncEnumerable().ToImmutableListAsync();
28+
Assert.True((await res).SequenceEqual(xs));
29+
}
30+
31+
[Fact]
32+
public async Task ToImmutableList_Empty()
33+
{
34+
var xs = AsyncEnumerable.Empty<int>();
35+
var res = xs.ToImmutableListAsync();
36+
Assert.True((await res).Count == 0);
37+
}
38+
39+
[Fact]
40+
public async Task ToImmutableList_Throw()
41+
{
42+
var ex = new Exception("Bang!");
43+
var res = Throw<int>(ex).ToImmutableListAsync();
44+
await AssertThrowsAsync(res, ex);
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)