public static bool IsAsyncEnumerable(this ITypeSymbol typeSymbol, Compilation compilation, out AsyncEnumerableSymbols enumerableSymbols) { if (typeSymbol is null) { throw new ArgumentNullException(nameof(typeSymbol)); } if (compilation is null) { throw new ArgumentNullException(nameof(compilation)); } if (!typeSymbol.IsAsyncEnumerableType(compilation, out var getEnumerator)) { enumerableSymbols = default; return(false); } var isAsyncEnumerator = getEnumerator.ReturnType.IsAsyncEnumeratorType(compilation, out var current, out var moveNext, out var dispose); enumerableSymbols = new AsyncEnumerableSymbols(getEnumerator, new AsyncEnumeratorSymbols(current, moveNext, dispose)); return(isAsyncEnumerator); }
/// <summary> /// Gets a value indicating whether 'await foreach' considers <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> to be enumerable. /// </summary> /// <param name="typeSymbol">The <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> to test.</param> /// <param name="compilation">The <see cref="Microsoft.CodeAnalysis.Compilation"/> context.</param> /// <param name="enumerableSymbols">If methods returns <c>true</c>, contains information on the methods 'await foreach' will use to enumerate.</param> /// <param name="errors">Gets information on what error caused the method to return <c>false</c>.</param> /// <returns><c>true</c> if 'await foreach' considers <see cref="Microsoft.CodeAnalysis.ITypeSymbol"/> to be enumerable; otherwise, <c>false</c>.</returns> public static bool IsAsyncEnumerable(this ITypeSymbol typeSymbol, Compilation compilation, [NotNullWhen(true)] out AsyncEnumerableSymbols?enumerableSymbols, out Errors errors) { if (typeSymbol.TypeKind != TypeKind.Interface) { var getEnumerator = typeSymbol.GetPublicMethod("GetAsyncEnumerator", typeof(CancellationToken)) ?? typeSymbol.GetPublicMethod("GetAsyncEnumerator"); if (getEnumerator is not null) { var enumeratorType = getEnumerator.ReturnType; var current = enumeratorType.GetPublicReadProperty("Current"); if (current is null) { enumerableSymbols = default; errors = Errors.MissingCurrent; return(false); } var moveNext = enumeratorType.GetPublicMethod("MoveNextAsync"); if (moveNext is null) { enumerableSymbols = default; errors = Errors.MissingMoveNext; return(false); } _ = enumeratorType.IsAsyncDisposable(compilation, out var dispose); enumerableSymbols = new AsyncEnumerableSymbols( getEnumerator, new AsyncEnumeratorSymbols(current, moveNext) { DisposeAsync = dispose, IsValueType = getEnumerator.ReturnType.IsValueType, IsAsyncEnumeratorInterface = enumeratorType.TypeKind == TypeKind.Interface, } ); errors = Errors.None; return(true); } } var asyncEnumerableType = compilation.GetTypeByMetadataName("System.Collections.Generic.IAsyncEnumerable`1") !; if (typeSymbol.ImplementsInterface(asyncEnumerableType, out var genericArguments)) { var asyncEnumeratorType = compilation.GetTypeByMetadataName("System.Collections.Generic.IAsyncEnumerator`1") !.Construct(genericArguments[0]); var asyncDisposableType = compilation.GetTypeByMetadataName("System.IAsyncDisposable") !; enumerableSymbols = new AsyncEnumerableSymbols( asyncEnumerableType.GetPublicMethod("GetAsyncEnumerator", typeof(CancellationToken)) !, new AsyncEnumeratorSymbols( asyncEnumeratorType.GetPublicReadProperty("Current") !, asyncEnumeratorType.GetPublicMethod("MoveNextAsync", Type.EmptyTypes) !) { DisposeAsync = asyncDisposableType.GetPublicMethod("DisposeAsync", Type.EmptyTypes), IsAsyncEnumeratorInterface = true, }