diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 87df26ed04d42..b3a06862247d4 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1403,7 +1403,8 @@ protected virtual void VisitLocalFunctionUse( SyntaxNode syntax, bool isCall) { - if (isCall) + // Extern local function bodies are not visited, so ignore their state. + if (isCall && !symbol.IsExtern) { Join(ref State, ref localFunctionState.StateFromBottom); diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTests.cs index 93310750eb3fb..e920dd9b4c06a 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTests.cs @@ -6103,5 +6103,66 @@ public Base(int x){} Diagnostic(ErrorCode.ERR_ParamUnassigned, "C").WithArguments("a").WithLocation(1, 7) ); } + + [Fact] + public void OutParameterIsNotAssigned_LocalFunction() + { + var source = """ + class C + { + void M(out int i) + { + f(); + static void f() { } + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,10): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // void M(out int i) + Diagnostic(ErrorCode.ERR_ParamUnassigned, "M").WithArguments("i").WithLocation(3, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79437")] + public void OutParameterIsNotAssigned_LocalFunction_Extern() + { + var source = """ + using System.Runtime.InteropServices; + class C + { + void M(out int i) + { + f(); + [DllImport("test")] static extern void f(); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,10): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // void M(out int i) + Diagnostic(ErrorCode.ERR_ParamUnassigned, "M").WithArguments("i").WithLocation(4, 10)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79437")] + public void UnassignedVariable_LocalFunction_Extern() + { + var source = """ + using System.Runtime.InteropServices; + class C + { + void M() + { + int i; + f(); + i.ToString(); + [DllImport("test")] extern static void f(); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (8,9): error CS0165: Use of unassigned local variable 'i' + // i.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(8, 9)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/LocalFunctions.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/LocalFunctions.cs index 252ee8285432a..86af9b521ca4d 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/LocalFunctions.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/LocalFunctions.cs @@ -705,6 +705,25 @@ public static void Main() Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Local").WithArguments("Local").WithLocation(7, 14)); } + [Fact] + public void UnusedLocalFunc_Extern() + { + var comp = CreateCompilation(""" + using System.Runtime.InteropServices; + class C + { + public static void Main() + { + [DllImport("test")] static extern bool Local(); + } + } + """); + comp.VerifyDiagnostics( + // (6,48): warning CS8321: The local function 'Local' is declared but never used + // [DllImport("test")] static extern bool Local(); + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "Local").WithArguments("Local").WithLocation(6, 48)); + } + [Fact] public void UnassignedInStruct() {