Skip to content

Commit e6d795f

Browse files
authored
Expression trees: support optional arguments and named arguments in parameter order (#77972)
1 parent 453c711 commit e6d795f

22 files changed

+2221
-54
lines changed

src/Compilers/CSharp/Portable/CSharpResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4172,6 +4172,9 @@ You should consider suppressing the warning only if you're sure that you don't w
41724172
<data name="ERR_ExpressionTreeContainsNamedArgument" xml:space="preserve">
41734173
<value>An expression tree may not contain a named argument specification</value>
41744174
</data>
4175+
<data name="ERR_ExpressionTreeContainsNamedArgumentOutOfPosition" xml:space="preserve">
4176+
<value>An expression tree may not contain a named argument specification out of position</value>
4177+
</data>
41754178
<data name="ERR_ExpressionTreeContainsOptionalArgument" xml:space="preserve">
41764179
<value>An expression tree may not contain a call or invocation that uses optional arguments</value>
41774180
</data>

src/Compilers/CSharp/Portable/Errors/ErrorCode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,7 @@ internal enum ErrorCode
23972397
ERR_InitInExtension = 9304,
23982398
ERR_ModifierOnUnnamedReceiverParameter = 9305,
23992399
ERR_ExtensionTypeNameDisallowed = 9306,
2400+
ERR_ExpressionTreeContainsNamedArgumentOutOfPosition = 9307,
24002401

24012402
// Note: you will need to do the following after adding errors:
24022403
// 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)

src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,7 @@ or ErrorCode.ERR_InstanceMemberWithUnnamedExtensionsParameter
25132513
or ErrorCode.ERR_InitInExtension
25142514
or ErrorCode.ERR_ModifierOnUnnamedReceiverParameter
25152515
or ErrorCode.ERR_ExtensionTypeNameDisallowed
2516+
or ErrorCode.ERR_ExpressionTreeContainsNamedArgumentOutOfPosition
25162517
=> false,
25172518
};
25182519
#pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value.

src/Compilers/CSharp/Portable/Errors/MessageID.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ internal enum MessageID
301301
IDS_FeaturePartialEventsAndConstructors = MessageBase + 12852,
302302
IDS_FeatureExtensions = MessageBase + 12853,
303303
IDS_FeatureNullConditionalAssignment = MessageBase + 12854,
304+
IDS_FeatureExpressionOptionalAndNamedArguments = MessageBase + 12855,
304305
}
305306

306307
// Message IDs may refer to strings that need to be localized.
@@ -488,6 +489,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
488489
case MessageID.IDS_FeaturePartialEventsAndConstructors:
489490
case MessageID.IDS_FeatureExtensions:
490491
case MessageID.IDS_FeatureNullConditionalAssignment:
492+
case MessageID.IDS_FeatureExpressionOptionalAndNamedArguments:
491493
return LanguageVersion.Preview;
492494

493495
// C# 13.0 features.

src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ private void VisitCall(
307307
ImmutableArray<BoundExpression> arguments,
308308
ImmutableArray<RefKind> argumentRefKindsOpt,
309309
ImmutableArray<string> argumentNamesOpt,
310+
ImmutableArray<int> argsToParamsOpt,
310311
BitVector defaultArguments,
311312
BoundNode node)
312313
{
@@ -328,14 +329,22 @@ private void VisitCall(
328329
{
329330
Error(ErrorCode.ERR_ExpressionTreeContainsIndexedProperty, node);
330331
}
331-
else if (hasDefaultArgument(arguments, defaultArguments))
332+
else if (hasDefaultArgument(arguments, defaultArguments) &&
333+
!_compilation.IsFeatureEnabled(MessageID.IDS_FeatureExpressionOptionalAndNamedArguments))
332334
{
333335
Error(ErrorCode.ERR_ExpressionTreeContainsOptionalArgument, node);
334336
}
335-
else if (!argumentNamesOpt.IsDefaultOrEmpty)
337+
else if (!argumentNamesOpt.IsDefaultOrEmpty &&
338+
!_compilation.IsFeatureEnabled(MessageID.IDS_FeatureExpressionOptionalAndNamedArguments))
336339
{
337340
Error(ErrorCode.ERR_ExpressionTreeContainsNamedArgument, node);
338341
}
342+
else if (!argumentNamesOpt.IsDefaultOrEmpty &&
343+
hasNamedArgumentOutOfOrder(argsToParamsOpt))
344+
{
345+
Debug.Assert(_compilation.IsFeatureEnabled(MessageID.IDS_FeatureExpressionOptionalAndNamedArguments));
346+
Error(ErrorCode.ERR_ExpressionTreeContainsNamedArgumentOutOfPosition, node);
347+
}
339348
else if (IsComCallWithRefOmitted(method, arguments, argumentRefKindsOpt))
340349
{
341350
Error(ErrorCode.ERR_ComRefCallInExpressionTree, node);
@@ -366,6 +375,22 @@ static bool hasDefaultArgument(ImmutableArray<BoundExpression> arguments, BitVec
366375

367376
return false;
368377
}
378+
379+
static bool hasNamedArgumentOutOfOrder(ImmutableArray<int> argsToParamsOpt)
380+
{
381+
if (argsToParamsOpt.IsDefaultOrEmpty)
382+
{
383+
return false;
384+
}
385+
for (int i = 0; i < argsToParamsOpt.Length; i++)
386+
{
387+
if (argsToParamsOpt[i] != i)
388+
{
389+
return true;
390+
}
391+
}
392+
return false;
393+
}
369394
}
370395

371396
public override BoundNode Visit(BoundNode node)
@@ -474,7 +499,7 @@ public override BoundNode VisitCall(BoundCall node)
474499

475500
do
476501
{
477-
VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
502+
visitCall(node);
478503
CheckReferenceToMethodIfLocalFunction(node, node.Method);
479504
this.VisitList(node.Arguments);
480505
}
@@ -484,14 +509,19 @@ public override BoundNode VisitCall(BoundCall node)
484509
}
485510
else
486511
{
487-
VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
512+
visitCall(node);
488513
CheckReceiverIfField(node.ReceiverOpt);
489514
CheckReferenceToMethodIfLocalFunction(node, node.Method);
490515
this.Visit(node.ReceiverOpt);
491516
this.VisitList(node.Arguments);
492517
}
493518

494519
return null;
520+
521+
void visitCall(BoundCall node)
522+
{
523+
VisitCall(node.Method, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.ArgsToParamsOpt, node.DefaultArguments, node);
524+
}
495525
}
496526

497527
/// <summary>
@@ -520,13 +550,13 @@ public override BoundNode VisitCollectionElementInitializer(BoundCollectionEleme
520550
Error(ErrorCode.ERR_ExtensionCollectionElementInitializerInExpressionTree, node);
521551
}
522552

523-
VisitCall(node.AddMethod, null, node.Arguments, default(ImmutableArray<RefKind>), default(ImmutableArray<string>), node.DefaultArguments, node);
553+
VisitCall(node.AddMethod, null, node.Arguments, default(ImmutableArray<RefKind>), default(ImmutableArray<string>), default(ImmutableArray<int>), node.DefaultArguments, node);
524554
return base.VisitCollectionElementInitializer(node);
525555
}
526556

527557
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node)
528558
{
529-
VisitCall(node.Constructor, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
559+
VisitCall(node.Constructor, null, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.ArgsToParamsOpt, node.DefaultArguments, node);
530560
return base.VisitObjectCreationExpression(node);
531561
}
532562

@@ -536,7 +566,7 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node)
536566
var method = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod();
537567
if ((object)method != null)
538568
{
539-
VisitCall(method, indexer, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.DefaultArguments, node);
569+
VisitCall(method, indexer, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.ArgsToParamsOpt, node.DefaultArguments, node);
540570
}
541571
CheckReceiverIfField(node.ReceiverOpt);
542572
return base.VisitIndexerAccess(node);

src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)