From 7b7c5ef2e4a20adfe3b280a456d520dea9f1aa0a Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Wed, 9 Sep 2020 06:29:03 -0700 Subject: [PATCH] Fix nullable analysis of lambdas within conditional expressions (#47405) --- .../Portable/FlowAnalysis/NullableWalker.cs | 7 ++ .../Semantics/NullableReferenceTypesTests.cs | 69 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 3bf8732ef0d..89766f5dbbb 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -4229,6 +4229,7 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu consequenceConversion, resultTypeWithAnnotations, consequenceRValue, + consequenceState, consequenceEndReachable); TypeWithState convertedAlternativeResult = convertResult( @@ -4237,6 +4238,7 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu alternativeConversion, resultTypeWithAnnotations, alternativeRValue, + alternativeState, alternativeEndReachable); resultState = convertedConsequenceResult.State.Join(convertedAlternativeResult.State); @@ -4272,8 +4274,12 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu Conversion conversion, TypeWithAnnotations targetType, TypeWithState operandType, + LocalState state, bool isReachable) { + var savedState = this.State; + this.State = state; + bool previousDisabledDiagnostics = _disableDiagnostics; // If the node is not reachable, then we're only visiting to get // nullability information for the public API, and not to produce diagnostics. @@ -4301,6 +4307,7 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu resultType = default; _disableDiagnostics = previousDisabledDiagnostics; } + this.State = savedState; return resultType; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 1c871fb210e..c1bb8d0075c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -51615,6 +51615,75 @@ static void F() Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "(I o) => { }").WithArguments("o", "lambda expression", "D>").WithLocation(10, 26)); } + [Fact, WorkItem(40561, "https://github.com/dotnet/roslyn/issues/40561")] + public void ReturnLambda_InsideConditionalExpr() + { + var source = @" +using System; + +class C +{ + static void M0(string s) { } + + static Action? M1(string? s) + => s != null + ? () => M0(s) + : (Action?)null; + + static Action? M2(string? s) + => s != null + ? (Action)(() => M0(s)) + : null; + + static Action? M3(string? s) + => s != null + ? () => { M0(s); s = null; } + : (Action?)null; + + static Action? M4(string? s) + { + return s != null + ? local(() => M0(s), s = null) + : (Action?)null; + + Action local(Action a1, string? s) + { + return a1; + } + } + + static Action? M5(string? s) + { + return s != null + ? local(s = null, () => M0(s)) // 1 + : (Action?)null; + + Action local(string? s, Action a1) + { + return a1; + } + } + + static Action? M6(string? s) + { + return s != null + ? local(() => M0(s)) + : (Action?)null; + + Action local(Action a1) + { + s = null; + return a1; + } + } +}"; + var comp = CreateNullableCompilation(source); + comp.VerifyDiagnostics( + // (38,40): warning CS8604: Possible null reference argument for parameter 's' in 'void C.M0(string s)'. + // ? local(s = null, () => M0(s)) // 1 + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("s", "void C.M0(string s)").WithLocation(38, 40)); + } + [Fact] [WorkItem(29617, "https://github.com/dotnet/roslyn/issues/29617")] public void ReturnTypeInference_01() -- GitLab