Skip to content

Add public API implementation for getting await info from await using and await foreach #80409

@333fred

Description

@333fred

Background and Motivation

Followup to #79818; in using the API in the SDK analyzers, we've realized that we also need async info for using declarations (both statement and local decl),
as well as await foreach. This adds APIs for accessing this information.

Proposed API

namespace Microsoft.CodeAnalysis.CSharp
{
    public static class CSharpExtensions
    {
+        public static AwaitExpressionInfo GetAwaitExpressionInfo(this SemanticModel? semanticModel, LocalDeclarationStatementSyntax awaitExpression);
+        public static AwaitExpressionInfo GetAwaitExpressionInfo(this SemanticModel? semanticModel, UsingStatementSyntax awaitExpression);
    }

    public class CSharpSemanticModel
    {
+        public AwaitExpressionInfo GetAwaitExpressionInfo(LocalDeclarationStatementSyntax awaitExpression);
+        public AwaitExpressionInfo GetAwaitExpressionInfo(UsingStatementSyntax awaitExpression);
    }

    public struct ForEachStatementInfo
    {
+        /// <summary>
+        /// Gets the awaitable info for the <see cref="MoveNextMethod"/> in an asynchronous foreach. <see langword="default"/> if this is a synchronous foreach.
+        /// </summary>
+        public AwaitExpressionInfo MoveNextAwaitableInfo { get; }

+        /// <summary>
+        /// Gets the awaitable info for the <see cref="DisposeMethod"/> in an asynchronous foreach. <see langword="default"/> if this is a synchronous foreach.
+        /// </summary>
+        public AwaitExpressionInfo DisposeAwaitableInfo { get; }
    }
}

Usage Examples

Await Using Declaration

// Analyze await using declaration
var tree = compilation.SyntaxTrees.First();
var model = compilation.GetSemanticModel(tree);
var awaitUsingDeclaration = tree.GetRoot()
    .DescendantNodes()
    .OfType<LocalDeclarationStatementSyntax>()
    .First(n => n.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword));

var awaitInfo = model.GetAwaitExpressionInfo(awaitUsingDeclaration);
Console.WriteLine($"GetAwaiter: {awaitInfo.GetAwaiterMethod}");
Console.WriteLine($"IsCompleted: {awaitInfo.IsCompletedProperty}");
Console.WriteLine($"GetResult: {awaitInfo.GetResultMethod}");

Await Using Statement

// Analyze await using statement
var awaitUsingStatement = tree.GetRoot()
    .DescendantNodes()
    .OfType<UsingStatementSyntax>()
    .First(n => n.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword));

var awaitInfo = model.GetAwaitExpressionInfo(awaitUsingStatement);
// Access awaitable information for the dispose operation

Await Foreach

// Analyze await foreach statement
var awaitForeachStatement = tree.GetRoot()
    .DescendantNodes()
    .OfType<ForEachStatementSyntax>()
    .First(n => n.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword));

var foreachInfo = model.GetForEachStatementInfo(awaitForeachStatement);

// Access awaitable info for MoveNextAsync()
var moveNextAwaitInfo = foreachInfo.MoveNextAwaitableInfo;
Console.WriteLine($"MoveNext GetAwaiter: {moveNextAwaitInfo.GetAwaiterMethod}");

// Access awaitable info for DisposeAsync()
var disposeAwaitInfo = foreachInfo.DisposeAwaitableInfo;
Console.WriteLine($"Dispose GetAwaiter: {disposeAwaitInfo.GetAwaiterMethod}");

Alternative Designs

Risks

Metadata

Metadata

Assignees

Labels

Area-CompilersConcept-APIThis issue involves adding, removing, clarification, or modification of an API.Feature - Runtime Asyncapi-approvedAPI was approved in API review, it can be implementedblockingAPI needs to reviewed with priority to unblock work

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions