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 @@ -898,9 +898,9 @@ 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;
var boundAwait = FindBoundAwait(GetLowerBoundNode(node), node)
bernd5 marked this conversation as resolved.
Show resolved Hide resolved
?? FindBoundAwait(GetUpperBoundNode(node), node);
var awaitableInfo = boundAwait?.AwaitableInfo;
if (awaitableInfo == null)
{
return default(AwaitExpressionInfo);
Expand All @@ -910,7 +910,40 @@ 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
);

static BoundAwaitExpression FindBoundAwait(BoundNode rootSearchNode, AwaitExpressionSyntax node)
{
var cur = rootSearchNode;
while (true)
{
switch (cur)
{
case BoundAwaitExpression ba:
if (ba.Syntax == node)
{
return ba;
}
else
{
return null;
}
case BoundExpressionStatement boundExpressionStatement:
cur = boundExpressionStatement.Expression;
break;
case BoundConversion boundConversion:
//e.g.: await something + "aText"
cur = boundConversion.Operand;
break;
case BoundBadStatement:
case BoundBadExpression:
return null;
default:
return null;
}
}
}
}

public override ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,97 @@ 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(){
MyAwaitable Get()
{
return new MyAwaitable();
}

var x = Get();
bernd5 marked this conversation as resolved.
Show resolved Hide resolved
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 refApis = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(
a => MetadataReference.CreateFromFile(a.Location)
);
var tree = SyntaxFactory.SyntaxTree(SyntaxFactory.ParseCompilationUnit(text));
var csCompilation = CSharpCompilation.Create("something",
new[] { tree },
refApis,
new CSharpCompilationOptions(OutputKind.ConsoleApplication)
);
bernd5 marked this conversation as resolved.
Show resolved Hide resolved

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

var op = model.GetOperation(awaitExpression);
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 Expand Up @@ -70,15 +161,17 @@ public void DefaultAwaitExpressionInfo()
Assert.Equal(0, info.GetHashCode());
}

private AwaitExpressionInfo GetAwaitExpressionInfo(string text, out CSharpCompilation compilation, params DiagnosticDescription[] diagnostics)
private AwaitExpressionInfo GetAwaitExpressionInfo(string text,
bernd5 marked this conversation as resolved.
Show resolved Hide resolved
out CSharpCompilation compilation, params DiagnosticDescription[] diagnostics)
{
var tree = Parse(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
var comp = CreateCompilationWithMscorlib45(new SyntaxTree[] { tree }, new MetadataReference[] { SystemRef });
comp.VerifyDiagnostics(diagnostics);
compilation = comp;
var syntaxNode = (AwaitExpressionSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.AwaitExpression).AsNode();
var treeModel = comp.GetSemanticModel(tree);
return treeModel.GetAwaitExpressionInfo(syntaxNode);
var awaitExpr = treeModel.GetAwaitExpressionInfo(syntaxNode);
return awaitExpr;
}

private AwaitExpressionInfo GetAwaitExpressionInfo(string text, params DiagnosticDescription[] diagnostics)
Expand Down