Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: GetAwaitExpressionInfo ignores BoundConversion #54296

Merged
merged 10 commits into from
Jun 24, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -899,8 +899,19 @@ public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax
throw new ArgumentException("node.Kind==" + node.Kind());
}

var bound = GetUpperBoundNode(node);
BoundAwaitableInfo awaitableInfo = (((bound as BoundExpressionStatement)?.Expression ?? bound) as BoundAwaitExpression)?.AwaitableInfo;
//usually the GetUpperBoundNode is sufficient, but we need to handle
//for example BoundExpressionStatement or BoundConversion here, too.
BoundAwaitExpression boundAwait = null;
foreach (var boundNode in GetBoundNodes(node))
bernd5 marked this conversation as resolved.
Show resolved Hide resolved
bernd5 marked this conversation as resolved.
Show resolved Hide resolved
{
if (boundNode is BoundAwaitExpression ba)
{
Debug.Assert(boundNode.Syntax == node);
boundAwait = ba;
break;
}
}
var awaitableInfo = boundAwait?.AwaitableInfo;
if (awaitableInfo == null)
{
return default(AwaitExpressionInfo);
Expand All @@ -910,7 +921,8 @@ public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax
getAwaiter: (IMethodSymbol)awaitableInfo.GetAwaiter?.ExpressionSymbol.GetPublicSymbol(),
isCompleted: awaitableInfo.IsCompleted.GetPublicSymbol(),
getResult: awaitableInfo.GetResult.GetPublicSymbol(),
isDynamic: awaitableInfo.IsDynamic);
isDynamic: awaitableInfo.IsDynamic
);
}

public override ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,85 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
/// </summary>
public class AwaitExpressionTests : CompilingTestBase
{
[Fact]
public void TestAwaitInfoExtensionMethod()
{
var text =
@"using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

static class App{
public static async Task Main(){
var x = new MyAwaitable();
x.SetValue(42);

Console.WriteLine(await x + ""!"");
}
}

struct MyAwaitable
{
private ValueTask<int> task;
private TaskCompletionSource<int> source;

private TaskCompletionSource<int> Source
{
get
{
if (source == null)
{
source = new TaskCompletionSource<int>();
task = new ValueTask<int>(source.Task);
}
return source;
}
}
internal ValueTask<int> Task
{
get
{
_ = Source;
return task;
}
}

public void SetValue(int i)
{
Source.SetResult(i);
}
}

static class MyAwaitableExtension
{
public static System.Runtime.CompilerServices.ValueTaskAwaiter<int> GetAwaiter(this MyAwaitable a)
{
return a.Task.GetAwaiter();
}
}";

var csCompilation = CreateCompilation(text, targetFramework: TargetFramework.NetCoreAppAndCSharp);
var tree = csCompilation.SyntaxTrees.Single();

var model = csCompilation.GetSemanticModel(tree);
var awaitExpression = tree.GetRoot().DescendantNodes().OfType<AwaitExpressionSyntax>().First();
bernd5 marked this conversation as resolved.
Show resolved Hide resolved

var info = model.GetAwaitExpressionInfo(awaitExpression);

Assert.Equal(
"System.Runtime.CompilerServices.ValueTaskAwaiter<System.Int32> MyAwaitableExtension.GetAwaiter(this MyAwaitable a)",
info.GetAwaiterMethod.ToTestDisplayString()
);
Assert.Equal(
"System.Int32 System.Runtime.CompilerServices.ValueTaskAwaiter<System.Int32>.GetResult()",
info.GetResultMethod.ToTestDisplayString()
);
Assert.Equal(
"System.Boolean System.Runtime.CompilerServices.ValueTaskAwaiter<System.Int32>.IsCompleted { get; }",
info.IsCompletedProperty.ToTestDisplayString()
);
}

[Fact]
[WorkItem(711413, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/711413")]
public void TestAwaitInfo()
Expand Down