Skip to content

Commit 49081fb

Browse files
authored
Do not make assumptions about content of erroneous BoundCall node based on value of InvokedAsExtensionMethod property (#80256)
Related to #78433.
1 parent 43dd4d9 commit 49081fb

File tree

13 files changed

+247
-68
lines changed

13 files changed

+247
-68
lines changed

src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3767,6 +3767,11 @@ internal SafeContext GetRefEscape(BoundExpression expr, SafeContext localScopeDe
37673767
{
37683768
var call = (BoundCall)expr;
37693769

3770+
if (call.IsErroneousNode)
3771+
{
3772+
return SafeContext.CallingMethod;
3773+
}
3774+
37703775
var methodSymbol = call.Method;
37713776
if (methodSymbol.RefKind == RefKind.None)
37723777
{
@@ -3826,6 +3831,11 @@ internal SafeContext GetRefEscape(BoundExpression expr, SafeContext localScopeDe
38263831
return SafeContext.CallingMethod;
38273832

38283833
case BoundCall call:
3834+
if (call.IsErroneousNode)
3835+
{
3836+
return SafeContext.CallingMethod;
3837+
}
3838+
38293839
var methodSymbol = call.Method;
38303840
if (methodSymbol.RefKind == RefKind.None)
38313841
{
@@ -4049,6 +4059,11 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext
40494059
{
40504060
var call = (BoundCall)expr;
40514061

4062+
if (call.IsErroneousNode)
4063+
{
4064+
return true;
4065+
}
4066+
40524067
var methodSymbol = call.Method;
40534068
if (methodSymbol.RefKind == RefKind.None)
40544069
{
@@ -4114,6 +4129,11 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext
41144129
return true;
41154130

41164131
case BoundCall call:
4132+
if (call.IsErroneousNode)
4133+
{
4134+
return true;
4135+
}
4136+
41174137
var methodSymbol = call.Method;
41184138
if (methodSymbol.RefKind == RefKind.None)
41194139
{
@@ -4411,6 +4431,11 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe
44114431
{
44124432
var call = (BoundCall)expr;
44134433

4434+
if (call.IsErroneousNode)
4435+
{
4436+
return SafeContext.CallingMethod;
4437+
}
4438+
44144439
return GetInvocationEscapeScope(
44154440
MethodInvocationInfo.FromCall(call),
44164441
localScopeDepth,
@@ -4457,6 +4482,11 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe
44574482
return localScopeDepth;
44584483

44594484
case BoundCall call:
4485+
if (call.IsErroneousNode)
4486+
{
4487+
return SafeContext.CallingMethod;
4488+
}
4489+
44604490
return GetInvocationEscapeScope(
44614491
MethodInvocationInfo.FromCall(call, implicitIndexerAccess.Receiver),
44624492
localScopeDepth,
@@ -5088,6 +5118,11 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext
50885118
case BoundKind.Call:
50895119
{
50905120
var call = (BoundCall)expr;
5121+
if (call.IsErroneousNode)
5122+
{
5123+
return true;
5124+
}
5125+
50915126
var methodSymbol = call.Method;
50925127

50935128
return CheckInvocationEscape(
@@ -5152,6 +5187,11 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext
51525187
return false;
51535188

51545189
case BoundCall call:
5190+
if (call.IsErroneousNode)
5191+
{
5192+
return true;
5193+
}
5194+
51555195
var methodSymbol = call.Method;
51565196

51575197
return CheckInvocationEscape(
@@ -5826,7 +5866,7 @@ SafeContext getPartsScope(BoundInterpolatedString interpolatedString, SafeContex
58265866

58275867
foreach (var part in interpolatedString.Parts)
58285868
{
5829-
if (part is not BoundCall call)
5869+
if (part is not BoundCall { IsErroneousNode: false } call)
58305870
{
58315871
// Dynamic calls cannot have ref struct parameters.
58325872
continue;
@@ -5868,7 +5908,7 @@ bool checkParts(BoundInterpolatedString interpolatedString, SafeContext escapeFr
58685908
{
58695909
foreach (var part in interpolatedString.Parts)
58705910
{
5871-
if (part is not BoundCall call)
5911+
if (part is not BoundCall { IsErroneousNode: false } call)
58725912
{
58735913
// Dynamic calls cannot have ref struct parameters.
58745914
continue;

src/Compilers/CSharp/Portable/Binder/Binder_Await.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ private bool CouldBeAwaited(BoundExpression expression)
9393
}
9494

9595
var call = (BoundCall)expression;
96+
Debug.Assert(!call.IsErroneousNode);
9697

9798
// First check if the target method is async.
9899
if ((object)call.Method != null && call.Method.IsAsync)
@@ -591,6 +592,8 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Bi
591592
}
592593

593594
var call = (BoundCall)getAwaiterCall;
595+
Debug.Assert(!call.IsErroneousNode);
596+
594597
var getAwaiterMethod = call.Method;
595598
if (getAwaiterMethod is ErrorMethodSymbol ||
596599
call.Expanded || HasOptionalParameters(getAwaiterMethod) || // We might have been able to resolve a GetAwaiter overload with optional parameters, so check for that here
@@ -699,6 +702,8 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no
699702
}
700703

701704
var call = (BoundCall)getAwaiterGetResultCall;
705+
Debug.Assert(!call.IsErroneousNode);
706+
702707
getResultMethod = call.Method;
703708
if (getResultMethod.IsExtensionMethod || getResultMethod.GetIsNewExtensionMember())
704709
{

src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ internal BoundExpression MakeInvocationExpression(
147147
// the error has already been reported by BindInvocationExpression
148148
Debug.Assert(diagnostics.DiagnosticBag is null || diagnostics.HasAnyErrors());
149149

150-
result = CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments);
150+
result = CreateBadCall(node, boundExpression, LookupResultKind.NotInvocable, analyzedArguments);
151151
}
152152

153153
result.WasCompilerGenerated = true;
@@ -369,7 +369,7 @@ private BoundExpression BindInvocationExpression(
369369
{
370370
if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, delegateType, node: node))
371371
{
372-
return CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments);
372+
return CreateBadCall(node, boundExpression, LookupResultKind.NotInvocable, analyzedArguments);
373373
}
374374

375375
result = BindDelegateInvocation(node, expression, methodName, boundExpression, analyzedArguments, diagnostics, queryClause, delegateType);

src/Compilers/CSharp/Portable/Binder/Binder_Query.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind
682682

683683
private static BoundExpression? ExtractCastInvocation(BoundCall invocation)
684684
{
685+
if (invocation.IsErroneousNode)
686+
{
687+
return null;
688+
}
689+
685690
int index = invocation.InvokedAsExtensionMethod ? 1 : 0;
686691
var c1 = invocation.Arguments[index] as BoundConversion;
687692
var l1 = c1 != null ? c1.Operand as BoundLambda : null;

src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -757,31 +757,60 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO
757757

758758
BeforeVisitingSkippedBoundCallChildren(node);
759759

760-
VisitReceiver(in methodInvocationInfo);
760+
visitReceiver(node, in methodInvocationInfo);
761761

762762
var nodeAndInvocationInfo = (call: node, methodInvocationInfo);
763763
do
764764
{
765-
VisitArguments(nodeAndInvocationInfo.call, in nodeAndInvocationInfo.methodInvocationInfo);
765+
visitArguments(nodeAndInvocationInfo.call, in nodeAndInvocationInfo.methodInvocationInfo);
766766
}
767767
while (calls.TryPop(out nodeAndInvocationInfo!));
768768

769769
calls.Free();
770770
}
771771
else
772772
{
773-
VisitReceiver(in methodInvocationInfo);
774-
VisitArguments(node, in methodInvocationInfo);
773+
visitReceiver(node, in methodInvocationInfo);
774+
visitArguments(node, in methodInvocationInfo);
775775
}
776776

777777
return null;
778778

779779
static MethodInvocationInfo getInvocationInfo(BoundCall node)
780780
{
781781
var methodInvocationInfo = MethodInvocationInfo.FromCall(node);
782-
methodInvocationInfo = ReplaceWithExtensionImplementationIfNeeded(in methodInvocationInfo);
782+
783+
if (!node.IsErroneousNode)
784+
{
785+
methodInvocationInfo = ReplaceWithExtensionImplementationIfNeeded(in methodInvocationInfo);
786+
}
787+
783788
return methodInvocationInfo;
784789
}
790+
791+
void visitReceiver(BoundCall node, ref readonly MethodInvocationInfo methodInvocationInfo)
792+
{
793+
if (node.IsErroneousNode)
794+
{
795+
Visit(node.ReceiverOpt);
796+
}
797+
else
798+
{
799+
VisitReceiver(in methodInvocationInfo);
800+
}
801+
}
802+
803+
void visitArguments(BoundCall node, ref readonly MethodInvocationInfo methodInvocationInfo)
804+
{
805+
if (node.IsErroneousNode)
806+
{
807+
VisitList(node.Arguments);
808+
}
809+
else
810+
{
811+
VisitArguments(node, in methodInvocationInfo);
812+
}
813+
}
785814
}
786815

787816
protected override void VisitReceiver(BoundCall node)
@@ -1092,7 +1121,7 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
10921121
return;
10931122
}
10941123

1095-
if (invocation.Method is null)
1124+
if (invocation.IsErroneousNode || invocation.Method is null)
10961125
{
10971126
return;
10981127
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics;
6+
7+
namespace Microsoft.CodeAnalysis.CSharp
8+
{
9+
internal partial class BoundCall
10+
{
11+
public bool IsErroneousNode => ResultKind is not LookupResultKind.Viable;
12+
13+
private partial void Validate()
14+
{
15+
Debug.Assert(ResultKind is not LookupResultKind.MemberGroup);
16+
Debug.Assert(ResultKind is not LookupResultKind.StaticInstanceMismatch);
17+
Debug.Assert(ResultKind is LookupResultKind.Viable || HasErrors);
18+
19+
/* Tracking issue: https://github.com/dotnet/roslyn/issues/79426
20+
Debug.Assert(ResultKind is LookupResultKind.Viable ||
21+
new StackTrace(fNeedFileInfo: false).GetFrame(2)?.GetMethod() switch
22+
{
23+
{ Name: nameof(ErrorCall), DeclaringType: { } declaringType } => declaringType == typeof(BoundCall),
24+
{ Name: nameof(Update), DeclaringType: { } declaringType } => declaringType == typeof(BoundCall),
25+
_ => false
26+
});
27+
*/
28+
}
29+
}
30+
}

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1823,7 +1823,7 @@
18231823
<Field Name="Properties" Type="ImmutableArray&lt;PropertySymbol&gt;" />
18241824
</Node>
18251825

1826-
<Node Name="BoundCall" Base="BoundExpression">
1826+
<Node Name="BoundCall" Base="BoundExpression" HasValidate="true">
18271827
<!-- Non-null type is required for this node kind -->
18281828
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>
18291829

@@ -1839,7 +1839,17 @@
18391839
<Field Name="InvokedAsExtensionMethod" Type="bool"/>
18401840
<Field Name="ArgsToParamsOpt" Type="ImmutableArray&lt;int&gt;" Null="allow"/>
18411841
<Field Name="DefaultArguments" Type="BitVector" />
1842+
1843+
<!--
1844+
Result kind for the invocation. Never LookupResultKind.MemberGroup or LookupResultKind.StaticInstanceMismatch.
1845+
If the value is something other than LookupResultKind.Viable, then the node represents a bad invocation,
1846+
the consistency among information stored in other properties is not guaranteed, and the Method property might
1847+
be set to an ErrorMethodSymbol. A special care should be taken when dealing with such nodes. It is recommended
1848+
to treat them similar to BoundBadExpressions nodes, unless there is a good reason to have more complicated
1849+
error recovery.
1850+
-->
18421851
<Field Name="ResultKind" PropertyOverrides="true" Type="LookupResultKind"/>
1852+
18431853
<!--The set of method symbols from which this call's method was chosen.
18441854
Only kept in the tree if the call was an error and overload resolution
18451855
was unable to choose a best method.-->

src/Compilers/CSharp/Portable/BoundTree/Constructors.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,15 @@ public static BoundCall ErrorCall(
132132
Binder binder)
133133
{
134134
if (!originalMethods.IsEmpty)
135+
{
135136
resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure);
137+
}
138+
else
139+
{
140+
Debug.Assert(method.OriginalDefinition is ErrorMethodSymbol);
141+
}
136142

143+
Debug.Assert(resultKind is not LookupResultKind.Viable);
137144
Debug.Assert(arguments.IsDefaultOrEmpty || (object)receiverOpt != (object)arguments[0]);
138145

139146
return new BoundCall(

0 commit comments

Comments
 (0)