From f5df3c74cc40671b9e114702b2a4f2d8ba948e37 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 3 May 2024 14:17:24 +0200 Subject: [PATCH 1/3] Add a test --- .../Semantics/NullableReferenceTypesTests.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 81d33246c8d68..b3a63c5418ccc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -158692,5 +158692,45 @@ static void M() where T : struct, I // y.Value.Item.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Value.Item").WithLocation(15, 9)); } + + [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2045970")] + public void BadAsyncLambdaInNamedArgument() + { + var source = """ + #nullable enable + using System; + using System.Threading.Tasks; + + class D + { + void M() + { + var c1 = new C(); + var c2 = new C(); + C.M1(f: async () => + { + if (c2 != null) + { + c1. + await c2.M2(); + } + }); + } + } + + class C + { + public static void M1(Func f) { } + public async Task M2() => await Task.Yield(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (15,20): error CS1001: Identifier expected + // c1. + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(15, 20), + // (15,20): error CS1002: ; expected + // c1. + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(15, 20)); + } } } From 880ff45bd48bc150cc33da7051d6476f1e7ce440 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 3 May 2024 15:00:49 +0200 Subject: [PATCH 2/3] Continue visiting bad named argument if not target-typed --- .../Portable/FlowAnalysis/NullableWalker.cs | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 91dc066983043..8c32f2b5bcab1 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6783,27 +6783,26 @@ private ImmutableArray VisitArguments( (ParameterSymbol? parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool isExpandedParamsArgument) = GetCorrespondingParameter(i, parametersOpt, argsToParamsOpt, expanded, ref paramsIterationType); - if (// This is known to happen for certain error scenarios, because - // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` - // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. - parameter is null || - // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type - // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. - node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true }) + // This is known to happen for certain error scenarios, because + // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` + // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. + if (parameter is null) { - if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + if (tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) { - // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it - // (we won't be able to complete that continuation) - // We flush the completion with a plausible/dummy type and remove it. - completion(TypeWithAnnotations.Create(argument.Type)); - TargetTypedAnalysisCompletion.Remove(argumentNoConversion); - Debug.Assert(parameter is not null || method is ErrorMethodSymbol); } continue; } + // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type + // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. + if (node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true } && + tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) + { + continue; + } + // We disable diagnostics when: // 1. the containing call has errors (to reduce cascading diagnostics) // 2. on implicit default arguments (since that's really only an issue with the declaration) @@ -6990,6 +6989,21 @@ static void expandParamsCollection(ref ImmutableArray arguments } } } + + bool tryShortCircuitTargetTypedExpression(BoundExpression argument, BoundExpression argumentNoConversion) + { + if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + { + // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it + // (we won't be able to complete that continuation) + // We flush the completion with a plausible/dummy type and remove it. + completion(TypeWithAnnotations.Create(argument.Type)); + TargetTypedAnalysisCompletion.Remove(argumentNoConversion); + return true; + } + + return false; + } } private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbol? method) From 436306626dee516da19d7f264e86d4796525c435 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Mon, 6 May 2024 09:56:54 +0200 Subject: [PATCH 3/3] Simplify an assert --- src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 8c32f2b5bcab1..14ec52bcddb6c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6790,7 +6790,7 @@ private ImmutableArray VisitArguments( { if (tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) { - Debug.Assert(parameter is not null || method is ErrorMethodSymbol); + Debug.Assert(method is ErrorMethodSymbol); } continue; }