diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index 669eb4a333560..a7afc4065669b 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -1,5 +1,35 @@ # This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7. +## For the purpose of definite assignment analysis, invocations of async local functions are no longer treated as being awaited + +***Introduced in Visual Studio 2022 version 17.5*** + +For the purpose of definite assignment analysis, invocations of an async local function is +no longer treated as being awaited and, therefore, the local function is not considered to +be fully executed. See https://github.com/dotnet/roslyn/issues/43697 for the rationale. + +The code below is now going to report a definite assignment error: +```csharp + public async Task M() + { + bool a; + await M1(); + Console.WriteLine(a); // error CS0165: Use of unassigned local variable 'a' + + async Task M1() + { + if ("" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + } + } +``` + ## `INoneOperation` nodes for attributes are now `IAttributeOperation` nodes. ***Introduced in Visual Studio 2022 version 17.5, .NET SDK version 7.0.200*** diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 763f55f12b794..a7ec1faf78b4e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1320,7 +1320,11 @@ protected virtual void VisitLocalFunctionUse( if (isCall) { Join(ref State, ref localFunctionState.StateFromBottom); - Meet(ref State, ref localFunctionState.StateFromTop); + + if (!symbol.IsAsync) + { + Meet(ref State, ref localFunctionState.StateFromTop); + } } localFunctionState.Visited = true; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index fa1d3c033c89e..546d49a7fea95 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -1481,6 +1481,9 @@ async void L1() } }" + IAsyncDisposableDefinition); comp.VerifyDiagnostics( + // (9,9): error CS0165: Use of unassigned local variable 'x' + // x++; + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(9, 9), // (10,9): error CS0165: Use of unassigned local variable 'y' // y++; Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(10, 9), diff --git a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/LocalFunctions.cs b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/LocalFunctions.cs index 2a374ace2b087..5fba019ebd8c8 100644 --- a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/LocalFunctions.cs +++ b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/LocalFunctions.cs @@ -428,6 +428,9 @@ async void L1() } }"); comp.VerifyDiagnostics( + // (11,9): error CS0165: Use of unassigned local variable 'x' + // x++; + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(11, 9), // (12,9): error CS0165: Use of unassigned local variable 'y' // y++; Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(12, 9), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 26125f169d6d0..5695d79762d06 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -10068,5 +10068,189 @@ int P Assert.Null(model.GetSymbolInfo(node).Symbol); } + + [Fact, WorkItem(43697, "https://github.com/dotnet/roslyn/issues/43697")] + public void DefiniteAssignment_01() + { + var text = @" +using System; +using System.Threading.Tasks; + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. +#pragma warning disable 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. + +public class C +{ + public void M() + { + bool a; + M1(); + Console.WriteLine(a); + async Task M1() + { + if ("""" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + } + } +} +"; + CreateCompilation(text).VerifyDiagnostics( + // (14,27): error CS0165: Use of unassigned local variable 'a' + // Console.WriteLine(a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(14, 27) + ); + } + + [Fact, WorkItem(43697, "https://github.com/dotnet/roslyn/issues/43697")] + public void DefiniteAssignment_02() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class C +{ + public void M() + { + bool a; + M1(); + Console.WriteLine(a); + Task M1() + { + if ("""" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + + return null; + } + } +} +"; + CreateCompilation(text).VerifyDiagnostics(); + } + + [Fact, WorkItem(43697, "https://github.com/dotnet/roslyn/issues/43697")] + public void DefiniteAssignment_03() + { + var text = @" +using System; +using System.Threading.Tasks; + +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. + +public class C +{ + public void M() + { + bool a; + M1(); + Console.WriteLine(a); + async Task M1() + { + await Task.Yield(); + + if ("""" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + } + } +} +"; + CreateCompilation(text).VerifyDiagnostics( + // (13,27): error CS0165: Use of unassigned local variable 'a' + // Console.WriteLine(a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(13, 27) + ); + } + + [Fact, WorkItem(43697, "https://github.com/dotnet/roslyn/issues/43697")] + public void DefiniteAssignment_04() + { + var text = @" +using System; +using System.Threading.Tasks; + +#pragma warning disable 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. + +public class C +{ + public async Task M() + { + bool a; + await M1(); + Console.WriteLine(a); + async Task M1() + { + if ("""" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + } + } +} +"; + CreateCompilation(text).VerifyDiagnostics( + // (13,27): error CS0165: Use of unassigned local variable 'a' + // Console.WriteLine(a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(13, 27) + ); + } + + [Fact, WorkItem(43697, "https://github.com/dotnet/roslyn/issues/43697")] + public void DefiniteAssignment_05() + { + var text = @" +using System; +using System.Threading.Tasks; + +public class C +{ + public async Task M() + { + bool a; + await M1(); + Console.WriteLine(a); + async Task M1() + { + await Task.Yield(); + + if ("""" == String.Empty) + { + throw new Exception(); + } + else + { + a = true; + } + } + } +} +"; + CreateCompilation(text).VerifyDiagnostics( + // (11,27): error CS0165: Use of unassigned local variable 'a' + // Console.WriteLine(a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(11, 27) + ); + } } }