From 678d2244029c67a60bd2bea82f28ae78db4b027b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 28 Oct 2024 17:36:24 -0700 Subject: [PATCH 1/6] Baseline for 'yield return' change --- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index a607dbf9aa09..0be61f3dc107 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -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; @@ -8672,5 +8673,142 @@ 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] + public void LambdaWithBindingErrorInYieldReturn() + { + var src = """ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + //comp.VerifyDiagnostics( + // // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // // static async IAsyncEnumerable>> BarAsync() + // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63)); + + src = """ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +class C +{ + static async IAsyncEnumerable>> BarAsync() + { + yield return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + + comp.VerifyDiagnostics( + // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async IAsyncEnumerable>> BarAsync() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63), + // (9,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // yield return async s => + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 30), + // (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)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "? s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void LambdaWithBindingErrorInReturn() + { + var src = """ +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + //comp.VerifyDiagnostics( + // // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // // static async Task>> BarAsync() + // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51)); + + src = """ +using System; +using System.Threading.Tasks; + +class C +{ + static async Task>> BarAsync() + { + return async s => + { + s // 1 + await Task.CompletedTask; + throw new NotImplementedException(); + }; + } +} +"""; + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics( + // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task>> BarAsync() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51), + // (8,24): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // return async s => + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(8, 24), + // (11,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(11, 13), + // (11,13): warning CS0168: The variable 'await' is declared but never used + // await Task.CompletedTask; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(11, 13), + // (11,19): error CS1002: ; expected + // await Task.CompletedTask; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(11, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var s = GetSyntax(tree, "s"); + Assert.Null(model.GetSymbolInfo(s).Symbol); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + } } } From 1754412bcb0788dae07f63df088be49bff043b48 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 28 Oct 2024 17:39:49 -0700 Subject: [PATCH 2/6] Adjust logic for 'yield return' --- .../Portable/Binder/Binder_Statements.cs | 29 ++++++++++++++----- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 15 ++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 0b4c03c79540..348f08993245 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -239,14 +239,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - if (!argument.HasAnyErrors) - { - argument = GenerateConversionForAssignment(elementType, argument, diagnostics); - } - else - { - argument = BindToTypeForErrorRecovery(argument); - } + bool hasErrors = false; // NOTE: it's possible that more than one of these conditions is satisfied and that // we won't report the syntactically innermost. However, dev11 appears to check @@ -254,25 +247,45 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi if (this.Flags.Includes(BinderFlags.InFinallyBlock)) { Error(diagnostics, ErrorCode.ERR_BadYieldInFinally, node.YieldKeyword); + hasErrors = true; } else if (this.Flags.Includes(BinderFlags.InTryBlockOfTryCatch)) { Error(diagnostics, ErrorCode.ERR_BadYieldInTryOfCatch, node.YieldKeyword); + hasErrors = true; } else if (this.Flags.Includes(BinderFlags.InCatchBlock)) { Error(diagnostics, ErrorCode.ERR_BadYieldInCatch, node.YieldKeyword); + hasErrors = true; } else if (BindingTopLevelScriptCode) { Error(diagnostics, ErrorCode.ERR_YieldNotAllowedInScript, node.YieldKeyword); + hasErrors = true; } else if (InUnsafeRegion && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) { Error(diagnostics, ErrorCode.ERR_BadYieldInUnsafe, node.YieldKeyword); + hasErrors = true; } CheckRequiredLangVersionForIteratorMethods(node, diagnostics); + + if (argument != null) + { + hasErrors |= argument.HasErrors || ((object)argument.Type != null && argument.Type.IsErrorType()); + } + + if (hasErrors) + { + argument = BindToTypeForErrorRecovery(argument); + } + else + { + argument = GenerateConversionForAssignment(elementType, argument, diagnostics); + } + return new BoundYieldReturnStatement(node, argument); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 0be61f3dc107..68a52ece5798 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -8727,6 +8727,9 @@ static async IAsyncEnumerable>> BarAsync() // (9,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // yield return async s => Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 30), + // (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), /* TODO2 */ // (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), @@ -8735,13 +8738,21 @@ static async IAsyncEnumerable>> BarAsync() 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)); + 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)); /* TODO2 */ + + // TODO2 + // Note: the ERR_BadSKknown and ERR_IllegalStatement binding errors are reported here, but not in the similar scenario with a `return` instead of a `yield return` + // In BindReturn scenario, we use CreateReturnConversion, which skips reporting from GenerateImplicitConversionError if argument.HasAnyErrors + // But in BindYieldReturn scenario, we use GenerateConversionForAssignment which uses GenerateImplicitConversionError unconditionally. var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var s = GetSyntax(tree, "s"); Assert.Null(model.GetSymbolInfo(s).Symbol); - Assert.Equal(new[] { "? s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(new[] { "System.String s" }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings()); } [Fact] From e97a01e7e1ab0e57101ebce699aa15f93878f4cf Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 28 Oct 2024 17:40:10 -0700 Subject: [PATCH 3/6] Adjust logic for 'return' --- .../Portable/Binder/Binder_Statements.cs | 2 +- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 35 ++++++++++--------- .../Semantics/PrimaryConstructorTests.cs | 5 ++- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 348f08993245..fccebe1187e0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3167,7 +3167,7 @@ internal BoundExpression CreateReturnConversion( diagnostics.Add(syntax, useSiteInfo); - if (!argument.HasAnyErrors) + if (!argument.HasAnyErrors || argument.Kind == BoundKind.UnboundLambda) { if (returnRefKind != RefKind.None) { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 68a52ece5798..6e2ddcc98244 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -8674,7 +8674,7 @@ static async System.Threading.Tasks.Task Main() comp1.VerifyEmitDiagnostics(); // Would call EnsureMetadataVirtual on S.DisposeAsync and would therefore assert if S was not already ForceCompleted } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68027")] public void LambdaWithBindingErrorInYieldReturn() { var src = """ @@ -8695,10 +8695,10 @@ static async IAsyncEnumerable>> BarAsync() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); - //comp.VerifyDiagnostics( - // // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // // static async IAsyncEnumerable>> BarAsync() - // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63)); + comp.VerifyDiagnostics( + // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async IAsyncEnumerable>> BarAsync() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63)); src = """ using System; @@ -8729,7 +8729,7 @@ static async IAsyncEnumerable>> BarAsync() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 30), // (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), /* TODO2 */ + 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), @@ -8741,12 +8741,7 @@ static async IAsyncEnumerable>> BarAsync() 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)); /* TODO2 */ - - // TODO2 - // Note: the ERR_BadSKknown and ERR_IllegalStatement binding errors are reported here, but not in the similar scenario with a `return` instead of a `yield return` - // In BindReturn scenario, we use CreateReturnConversion, which skips reporting from GenerateImplicitConversionError if argument.HasAnyErrors - // But in BindYieldReturn scenario, we use GenerateConversionForAssignment which uses GenerateImplicitConversionError unconditionally. + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -8775,10 +8770,10 @@ static async Task>> BarAsync() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); - //comp.VerifyDiagnostics( - // // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // // static async Task>> BarAsync() - // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51)); + comp.VerifyDiagnostics( + // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // static async Task>> BarAsync() + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51)); src = """ using System; @@ -8805,6 +8800,9 @@ static async Task>> BarAsync() // (8,24): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // return async s => Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(8, 24), + // (10,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(10, 13), // (11,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(11, 13), @@ -8813,7 +8811,10 @@ static async Task>> BarAsync() Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(11, 13), // (11,19): error CS1002: ; expected // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(11, 19)); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(11, 19), + // (11,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(11, 19)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs index ef70e6a58391..3559b208e34e 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PrimaryConstructorTests.cs @@ -9540,7 +9540,10 @@ class C1 (int p1) comp1.VerifyEmitDiagnostics( // (4,38): error CS1041: Identifier expected; 'delegate' is a keyword // public System.Func 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' does not take 1 arguments + // public System.Func M21() => delegate => p1; + Diagnostic(ErrorCode.ERR_BadDelArgCount, "=>").WithArguments("System.Func", "1").WithLocation(4, 47) ); var source = @" From d64d4c7de72bf5bf424e3ca2e5eb5e3665fdac91 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 29 Oct 2024 10:03:41 -0700 Subject: [PATCH 4/6] Revert unnecessary part of the change --- .../Portable/Binder/Binder_Statements.cs | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index fccebe1187e0..f1403e11b133 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -239,7 +239,14 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - bool hasErrors = false; + if (argument.HasErrors || ((object)argument.Type != null && argument.Type.IsErrorType())) + { + argument = BindToTypeForErrorRecovery(argument); + } + else + { + argument = GenerateConversionForAssignment(elementType, argument, diagnostics); + } // NOTE: it's possible that more than one of these conditions is satisfied and that // we won't report the syntactically innermost. However, dev11 appears to check @@ -247,45 +254,26 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi if (this.Flags.Includes(BinderFlags.InFinallyBlock)) { Error(diagnostics, ErrorCode.ERR_BadYieldInFinally, node.YieldKeyword); - hasErrors = true; } else if (this.Flags.Includes(BinderFlags.InTryBlockOfTryCatch)) { Error(diagnostics, ErrorCode.ERR_BadYieldInTryOfCatch, node.YieldKeyword); - hasErrors = true; } else if (this.Flags.Includes(BinderFlags.InCatchBlock)) { Error(diagnostics, ErrorCode.ERR_BadYieldInCatch, node.YieldKeyword); - hasErrors = true; } else if (BindingTopLevelScriptCode) { Error(diagnostics, ErrorCode.ERR_YieldNotAllowedInScript, node.YieldKeyword); - hasErrors = true; } else if (InUnsafeRegion && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) { Error(diagnostics, ErrorCode.ERR_BadYieldInUnsafe, node.YieldKeyword); - hasErrors = true; } CheckRequiredLangVersionForIteratorMethods(node, diagnostics); - if (argument != null) - { - hasErrors |= argument.HasErrors || ((object)argument.Type != null && argument.Type.IsErrorType()); - } - - if (hasErrors) - { - argument = BindToTypeForErrorRecovery(argument); - } - else - { - argument = GenerateConversionForAssignment(elementType, argument, diagnostics); - } - return new BoundYieldReturnStatement(node, argument); } From 85092abdd53ee97cbbb9cdce68e82e6826550682 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 29 Oct 2024 10:29:28 -0700 Subject: [PATCH 5/6] Suppress CS1998 --- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 66 ++++++++----------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 6e2ddcc98244..3dd1da3b3728 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -8678,6 +8678,7 @@ static async System.Threading.Tasks.Task Main() 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; @@ -8695,12 +8696,10 @@ static async IAsyncEnumerable>> BarAsync() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics( - // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // static async IAsyncEnumerable>> BarAsync() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63)); + 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; @@ -8721,27 +8720,21 @@ static async IAsyncEnumerable>> BarAsync() comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( - // (7,63): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // static async IAsyncEnumerable>> BarAsync() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(7, 63), - // (9,30): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // yield return async s => - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 30), - // (11,13): error CS0118: 's' is a variable but is used like a type + // (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(11, 13), - // (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + 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(12, 13), - // (12,13): warning CS0168: The variable 'await' is declared but never used + 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(12, 13), - // (12,19): error CS1002: ; expected + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(13, 13), + // (13,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 + 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(12, 19)); + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(13, 19)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -8754,6 +8747,7 @@ static async IAsyncEnumerable>> BarAsync() 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; @@ -8770,12 +8764,10 @@ static async Task>> BarAsync() } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics( - // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // static async Task>> BarAsync() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51)); + comp.VerifyDiagnostics(); src = """ +#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously using System; using System.Threading.Tasks; @@ -8794,27 +8786,21 @@ static async Task>> BarAsync() """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( - // (6,51): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // static async Task>> BarAsync() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "BarAsync").WithLocation(6, 51), - // (8,24): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. - // return async s => - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(8, 24), - // (10,13): error CS0118: 's' is a variable but is used like a type + // (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(10, 13), - // (11,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + 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(11, 13), - // (11,13): warning CS0168: The variable 'await' is declared but never used + 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(11, 13), - // (11,19): error CS1002: ; expected + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13), + // (12,19): error CS1002: ; expected // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(11, 19), - // (11,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + 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(11, 19)); + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); From d0ab7f58f34dc3174a106010c75729ac27ad7c03 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 29 Oct 2024 10:33:21 -0700 Subject: [PATCH 6/6] Minimize diff --- src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index f1403e11b133..fe05583f0f34 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -239,13 +239,13 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi ? BadExpression(node).MakeCompilerGenerated() : BindValue(node.Expression, diagnostics, BindValueKind.RValue); - if (argument.HasErrors || ((object)argument.Type != null && argument.Type.IsErrorType())) + if (!argument.HasErrors && ((object)argument.Type == null || !argument.Type.IsErrorType())) { - argument = BindToTypeForErrorRecovery(argument); + argument = GenerateConversionForAssignment(elementType, argument, diagnostics); } else { - argument = GenerateConversionForAssignment(elementType, argument, diagnostics); + argument = BindToTypeForErrorRecovery(argument); } // NOTE: it's possible that more than one of these conditions is satisfied and that @@ -273,7 +273,6 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi } CheckRequiredLangVersionForIteratorMethods(node, diagnostics); - return new BoundYieldReturnStatement(node, argument); }