Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,6 @@ internal static bool HasAnyBody(this BaseMethodDeclarationSyntax declaration)
return (declaration.Body ?? (SyntaxNode?)declaration.ExpressionBody) != null;
}

#nullable enable

internal static bool IsTopLevelStatement([NotNullWhen(true)] GlobalStatementSyntax? syntax)
{
return syntax?.Parent?.IsKind(SyntaxKind.CompilationUnit) == true;
Expand All @@ -507,8 +505,19 @@ internal static bool IsSimpleProgramTopLevelStatement(GlobalStatementSyntax? syn
internal static bool HasAwaitOperations(SyntaxNode node)
{
// Do not descend into functions
return node.DescendantNodesAndSelf(child => !IsNestedFunction(child)).
OfType<AwaitExpressionSyntax>().Any(); // PROTOTYPE(SimplePrograms): Recognize other async operations.
return node.DescendantNodesAndSelf(child => !IsNestedFunction(child)).Any(node =>
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the indentation is really unfortunate for reviewing on github.

switch (node)
{
case AwaitExpressionSyntax _:
case LocalDeclarationStatementSyntax local when local.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
case CommonForEachStatementSyntax @foreach when @foreach.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
case UsingStatementSyntax @using when @using.AwaitKeyword.IsKind(SyntaxKind.AwaitKeyword):
return true;
default:
return false;
}
});
}

internal static bool IsNestedFunction(SyntaxNode child)
Expand Down
122 changes: 122 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/SimpleProgramsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,128 @@ public void Dispose()
CompileAndVerify(comp, expectedOutput: "12", verify: Verification.Skipped);
}

[Fact]
public void LocalDeclarationStatement_10()
{
string source = @"
await using var x = new C();
System.Console.Write(""body "");

class C : System.IAsyncDisposable, System.IDisposable
{
public System.Threading.Tasks.ValueTask DisposeAsync()
{
System.Console.Write(""DisposeAsync"");
return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask);
}
public void Dispose()
{
System.Console.Write(""IGNORED"");
}
}
";
var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "body DisposeAsync");
}

[Fact]
public void UsingStatement_01()
{
string source = @"
await using (var x = new C())
{
System.Console.Write(""body "");
}

class C : System.IAsyncDisposable, System.IDisposable
{
public System.Threading.Tasks.ValueTask DisposeAsync()
{
System.Console.Write(""DisposeAsync"");
return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask);
}
public void Dispose()
{
System.Console.Write(""IGNORED"");
}
}
";
var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "body DisposeAsync");
}

[Fact]
public void UsingStatement_02()
{
string source = @"
await using (new C())
{
System.Console.Write(""body "");
}

class C : System.IAsyncDisposable, System.IDisposable
{
public System.Threading.Tasks.ValueTask DisposeAsync()
{
System.Console.Write(""DisposeAsync"");
return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask);
}
public void Dispose()
{
System.Console.Write(""IGNORED"");
}
}
";
var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "body DisposeAsync");
}

[Fact]
public void ForeachStatement_01()
{
string source = @"
using System.Threading.Tasks;

await foreach (var i in new C())
{
}

System.Console.Write(""Done"");

class C
{
public Enumerator GetAsyncEnumerator()
{
return new Enumerator();
}
public sealed class Enumerator
{
public async Task<bool> MoveNextAsync()
{
System.Console.Write(""MoveNextAsync "");
await Task.Yield();
return false;
}
public int Current
{
get => throw null;
}
public async Task DisposeAsync()
{
System.Console.Write(""DisposeAsync "");
await Task.Yield();
}
}
}
";
var comp = CreateCompilationWithTasksExtensions(new[] { source, s_IAsyncEnumerable }, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "MoveNextAsync DisposeAsync Done");
}

[Fact]
public void LocalUsedBeforeDeclaration_01()
{
Expand Down