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

Bind lambda in yield return despite errors #75660

Merged
merged 6 commits into from
Oct 30, 2024
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
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi
? BadExpression(node).MakeCompilerGenerated()
: BindValue(node.Expression, diagnostics, BindValueKind.RValue);

if (!argument.HasAnyErrors)
if (!argument.HasErrors && ((object)argument.Type == null || !argument.Type.IsErrorType()))
{
argument = GenerateConversionForAssignment(elementType, argument, diagnostics);
}
Expand Down Expand Up @@ -3154,7 +3154,7 @@ internal BoundExpression CreateReturnConversion(

diagnostics.Add(syntax, useSiteInfo);

if (!argument.HasAnyErrors)
if (!argument.HasAnyErrors || argument.Kind == BoundKind.UnboundLambda)
jcouv marked this conversation as resolved.
Show resolved Hide resolved
{
if (returnRefKind != RefKind.None)
{
Expand Down
136 changes: 136 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Text;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
Expand Down Expand Up @@ -8672,5 +8673,140 @@ static async System.Threading.Tasks.Task Main()
comp2.VerifyEmitDiagnostics(); // Indirectly calling IsMetadataVirtual on S.DisposeAsync (a read which causes the lock to be set)
comp1.VerifyEmitDiagnostics(); // Would call EnsureMetadataVirtual on S.DisposeAsync and would therefore assert if S was not already ForceCompleted
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68027")]
public void LambdaWithBindingErrorInYieldReturn()
{
var src = """
#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class C
{
static async IAsyncEnumerable<Func<string, Task<string>>> BarAsync()
{
yield return async s =>
{
await Task.CompletedTask;
throw new NotImplementedException();
};
}
}
""";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics();

src = """
#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class C
{
static async IAsyncEnumerable<Func<string, Task<string>>> BarAsync()
{
yield return async s =>
{
s // 1
await Task.CompletedTask;
throw new NotImplementedException();
};
}
}
""";
comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);

comp.VerifyDiagnostics(
// (12,13): error CS0118: 's' is a variable but is used like a type
// s // 1
Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(12, 13),
// (13,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(13, 13),
// (13,13): warning CS0168: The variable 'await' is declared but never used
// await Task.CompletedTask;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(13, 13),
// (13,19): error CS1002: ; expected
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(13, 19),
// (13,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(13, 19));

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var s = GetSyntax<IdentifierNameSyntax>(tree, "s");
Assert.Null(model.GetSymbolInfo(s).Symbol);
Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings());
}

[Fact]
public void LambdaWithBindingErrorInReturn()
{
var src = """
#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously
using System;
using System.Threading.Tasks;

class C
{
static async Task<Func<string, Task<string>>> BarAsync()
{
return async s =>
{
await Task.CompletedTask;
throw new NotImplementedException();
};
}
}
""";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics();

src = """
#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously
using System;
using System.Threading.Tasks;

class C
{
static async Task<Func<string, Task<string>>> BarAsync()
{
return async s =>
{
s // 1
await Task.CompletedTask;
throw new NotImplementedException();
};
}
}
""";
comp = CreateCompilation(src, targetFramework: TargetFramework.Net80);
comp.VerifyDiagnostics(
// (11,13): error CS0118: 's' is a variable but is used like a type
// s // 1
Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(11, 13),
// (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 13),
// (12,13): warning CS0168: The variable 'await' is declared but never used
// await Task.CompletedTask;
Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13),
// (12,19): error CS1002: ; expected
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(12, 19),
// (12,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
// await Task.CompletedTask;
Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19));

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var s = GetSyntax<IdentifierNameSyntax>(tree, "s");
Assert.Null(model.GetSymbolInfo(s).Symbol);
Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9540,7 +9540,10 @@ class C1 (int p1)
comp1.VerifyEmitDiagnostics(
// (4,38): error CS1041: Identifier expected; 'delegate' is a keyword
// public System.Func<int> M21() => delegate => p1;
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38)
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "delegate").WithArguments("", "delegate").WithLocation(4, 38),
// (4,47): error CS1593: Delegate 'Func<int>' does not take 1 arguments
// public System.Func<int> M21() => delegate => p1;
Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Func<int>", "1").WithLocation(4, 47)
);

var source = @"
Expand Down