From ab6f95fb6c893096b74d8126300bdb7ceda1c801 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 07:22:37 -0800 Subject: [PATCH 01/92] Add CFG and IOp --- .../Operations/CSharpOperationFactory.cs | 16 ++++++++- .../Generated/Operations.Generated.cs | 35 +++++++++++++++---- .../Operations/ControlFlowGraphBuilder.cs | 5 +++ .../Operations/OperationInterfaces.xml | 11 +++++- .../Core/Portable/PublicAPI.Unshipped.txt | 1 + 5 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 4c46478c895f3..f76e91d69ee6f 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1222,13 +1222,15 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundCollectionExpression expr) { + var compilation = (CSharpCompilation)_semanticModel.Compilation; SyntaxNode syntax = expr.Syntax; ITypeSymbol? collectionType = expr.GetPublicTypeSymbol(); bool isImplicit = expr.WasCompilerGenerated; - IMethodSymbol? constructMethod = getConstructMethod((CSharpCompilation)_semanticModel.Compilation, expr).GetPublicSymbol(); + IMethodSymbol? constructMethod = getConstructMethod(compilation, expr).GetPublicSymbol(); ImmutableArray elements = expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr); return new CollectionExpressionOperation( constructMethod, + getCreationArguments(), elements, _semanticModel, syntax, @@ -1253,6 +1255,18 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl throw ExceptionUtilities.UnexpectedValue(expr.CollectionTypeKind); } } + + ImmutableArray getCreationArguments() + { + var collectionCreation = expr.CollectionCreation; + while (collectionCreation is BoundConversion conversion) + collectionCreation = conversion.Operand; + + if (collectionCreation is BoundCall or BoundObjectCreationExpression) + return DeriveArguments(collectionCreation); + + return []; + } } private IOperation CreateBoundCollectionExpressionElement(BoundCollectionExpression expr, BoundNode element) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index bc88d5fe9eff7..26b49df9067c7 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3955,6 +3955,12 @@ public interface ICollectionExpressionOperation : IOperation /// IMethodSymbol? ConstructMethod { get; } /// + /// Arguments passed to a with(...) element on the collection expression, if any. + /// If the collection expression does not have a with(...) element, or does not allow + /// any arguments, this can be an empty array. + /// + ImmutableArray CreationArguments { get; } + /// /// Collection expression elements. /// /// If the element is an expression, the entry is the expression, with a conversion to @@ -10672,21 +10678,26 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i } internal sealed partial class CollectionExpressionOperation : Operation, ICollectionExpressionOperation { - internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray creationArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) : base(semanticModel, syntax, isImplicit) { ConstructMethod = constructMethod; + CreationArguments = SetParentOperation(creationArguments, this); Elements = SetParentOperation(elements, this); Type = type; } public IMethodSymbol? ConstructMethod { get; } + public ImmutableArray CreationArguments { get; } public ImmutableArray Elements { get; } internal override int ChildOperationsCount => + CreationArguments.Length + Elements.Length; internal override IOperation GetCurrent(int slot, int index) => slot switch { - 0 when index < Elements.Length + 0 when index < CreationArguments.Length + => CreationArguments[index], + 1 when index < Elements.Length => Elements[index], _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), }; @@ -10695,13 +10706,18 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previ switch (previousSlot) { case -1: - if (!Elements.IsEmpty) return (true, 0, 0); + if (!CreationArguments.IsEmpty) return (true, 0, 0); else goto case 0; - case 0 when previousIndex + 1 < Elements.Length: + case 0 when previousIndex + 1 < CreationArguments.Length: return (true, 0, previousIndex + 1); case 0: + if (!Elements.IsEmpty) return (true, 1, 0); + else goto case 1; + case 1 when previousIndex + 1 < Elements.Length: + return (true, 1, previousIndex + 1); case 1: - return (false, 1, 0); + case 2: + return (false, 2, 0); default: throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); } @@ -10711,7 +10727,12 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i switch (previousSlot) { case int.MaxValue: - if (!Elements.IsEmpty) return (true, 0, Elements.Length - 1); + if (!Elements.IsEmpty) return (true, 1, Elements.Length - 1); + else goto case 1; + case 1 when previousIndex > 0: + return (true, 1, previousIndex - 1); + case 1: + if (!CreationArguments.IsEmpty) return (true, 0, CreationArguments.Length - 1); else goto case 0; case 0 when previousIndex > 0: return (true, 0, previousIndex - 1); @@ -11400,7 +11421,7 @@ public override IOperation VisitInlineArrayAccess(IInlineArrayAccessOperation op public override IOperation VisitCollectionExpression(ICollectionExpressionOperation operation, object? argument) { var internalOperation = (CollectionExpressionOperation)operation; - return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.CreationArguments), VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } public override IOperation VisitSpread(ISpreadOperation operation, object? argument) { diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 753a206354748..9192e50575ef0 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6542,6 +6542,10 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = PushStackFrame(); + var creationArguments = VisitArguments(operation.CreationArguments, instancePushed: false); + PopStackFrame(frame); + + frame = PushStackFrame(); var elements = VisitArray( operation.Elements, unwrapper: static (IOperation element) => @@ -6565,6 +6569,7 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize PopStackFrame(frame); return new CollectionExpressionOperation( operation.ConstructMethod, + creationArguments, elements, semanticModel: null, operation.Syntax, diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index f1d3f1af38be3..7cb1acf00aff7 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3669,7 +3669,7 @@ - + Represents a collection expression. @@ -3697,6 +3697,15 @@ + + + + Arguments passed to a with(...) element on the collection expression, if any. + If the collection expression does not have a with(...) element, or does not allow + any arguments, this can be an empty array. + + + diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index fc5b0a225448b..ac999afba3bd3 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,6 +21,7 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.ReduceExtensionMember(Microsoft.CodeAnalysis.ITypeSymbol! receiverType) -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.CreationArguments.get -> System.Collections.Immutable.ImmutableArray static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? From 47ffa73b7299430442aa2f3a0f1df97b7291d38c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 10:48:28 -0800 Subject: [PATCH 02/92] In progress --- .../Operations/CSharpOperationFactory.cs | 20 +++++++- ...ExpressionTests_WithElement_Constructor.cs | 4 +- ...ectionExpressionTests_WithElement_Extra.cs | 46 +------------------ .../Core/Compilation/OperationTreeVerifier.cs | 3 ++ .../Core/Compilation/TestOperationVisitor.cs | 2 +- .../Apis/Microsoft.CodeAnalysis.txt | 1 + 6 files changed, 27 insertions(+), 49 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index f76e91d69ee6f..ba5d3c973fbc0 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -323,6 +323,14 @@ public CSharpOperationFactory(SemanticModel semanticModel) _ => null }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); + case BoundKind.ValuePlaceholder: + return new PlaceholderOperation( + PlaceholderKind.Unspecified, _semanticModel, boundNode.Syntax, + boundNode switch + { + BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), + _ => null + }, boundNode.WasCompilerGenerated); case BoundKind.UnconvertedInterpolatedString: case BoundKind.UnconvertedConditionalOperator: case BoundKind.UnconvertedSwitchExpression: @@ -1262,9 +1270,19 @@ ImmutableArray getCreationArguments() while (collectionCreation is BoundConversion conversion) collectionCreation = conversion.Operand; - if (collectionCreation is BoundCall or BoundObjectCreationExpression) + if (collectionCreation is BoundObjectCreationExpression) return DeriveArguments(collectionCreation); + if (collectionCreation is BoundCall) + { + // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. + // We do *not* want to include that information in the Arguments we return. + var arguments = DeriveArguments(collectionCreation); + return arguments is [.. var normalArguments, _] + ? normalArguments + : arguments; + } + return []; } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs index d87127de2d4ea..61307bf9576da 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs @@ -392,9 +392,7 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().ToArray()[1]); VerifyOperationTree(compilation, operation, """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') - Elements(1): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + """); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index dd2946730ba90..8b9f7fff9234a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1536,15 +1536,7 @@ static void Main() var arrowExpressions = root.DescendantNodes().OfType().ToArray(); var operation1 = semanticModel.GetOperation(arrowExpressions[0]); VerifyOperationTree(compilation, operation1, """ - IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(), t]') - IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(), t]') - ReturnedValue: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(), t]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') - Elements(1): - IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') + """); var operation2 = semanticModel.GetOperation(arrowExpressions[1]); VerifyOperationTree(compilation, operation2, """ @@ -1593,41 +1585,7 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().Single()); VerifyOperationTree(compilation, operation, """ - IBlockOperation (2 statements, 2 locals) (OperationKind.Block, Type: null) (Syntax: '{ ... }') - Locals: Local_1: System.Collections.Generic.IList x - Local_2: System.Collections.Generic.IList y - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'IList ... , 1, 2, 3];') - IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'IList ... ), 1, 2, 3]') - Declarators: - IVariableDeclaratorOperation (Symbol: System.Collections.Generic.IList x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = [with(), 1, 2, 3]') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= [with(), 1, 2, 3]') - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(), 1, 2, 3]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(), 1, 2, 3]') - Elements(3): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') - Initializer: - null - IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'IList ... , 1, 2, 3];') - IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'IList ... ), 1, 2, 3]') - Declarators: - IVariableDeclaratorOperation (Symbol: System.Collections.Generic.IList y) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'y = [with(c ... ), 1, 2, 3]') - Initializer: - IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= [with(cap ... ), 1, 2, 3]') - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(capac ... ), 1, 2, 3]') - Elements(3): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') - Initializer: - null + """); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 8c97ecf3362ba..5c2c86720be13 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1615,6 +1615,9 @@ public override void VisitCollectionExpression(ICollectionExpressionOperation op LogString(")"); LogCommonPropertiesAndNewLine(operation); + if (operation.CreationArguments.Length > 0) + VisitArray(operation.CreationArguments, nameof(operation.CreationArguments), logElementCount: true); + VisitArray(operation.Elements, nameof(operation.Elements), logElementCount: true); } diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index 895618188c2b2..88dd697c3511a 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -488,7 +488,7 @@ internal override void VisitFixed(IFixedOperation operation) public override void VisitCollectionExpression(ICollectionExpressionOperation operation) { Assert.Equal(OperationKind.CollectionExpression, operation.Kind); - AssertEx.Equal(operation.Elements, operation.ChildOperations); + AssertEx.Equal([.. operation.CreationArguments, .. operation.Elements], operation.ChildOperations); } public override void VisitSpread(ISpreadOperation operation) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index c3da974bae4db..5b3c06fd5eebd 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -2014,6 +2014,7 @@ Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_Arg Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_IsDynamic Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructMethod +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_CreationArguments Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_Elements Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation.get_ConstrainedToType From e26f05bf73d2a8a2a6d37ca89ae87786c210f65d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 11:36:26 -0800 Subject: [PATCH 03/92] Fixes --- ...ExpressionTests_WithElement_Constructor.cs | 71 +++++++++++++++++-- ...ectionExpressionTests_WithElement_Extra.cs | 57 ++++++++++++++- 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs index 61307bf9576da..e63629f0ce36a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs @@ -392,7 +392,22 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().ToArray()[1]); VerifyOperationTree(compilation, operation, """ - +<<<<<<< Updated upstream + +======= + ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') + CreationArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: name) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 10)') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "default", IsImplicit) (Syntax: 'with(capacity: 10)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') +>>>>>>> Stashed changes """); } @@ -453,6 +468,15 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') + CreationArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 10') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: name) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 10)') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "default", IsImplicit) (Syntax: 'with(capacity: 10)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') Next (Regular) Block[B4] @@ -466,6 +490,15 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capac ... "both"), 4]') + CreationArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 20') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 20) (Syntax: '20') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: name) (OperationKind.Argument, Type: null) (Syntax: 'name: "both"') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "both") (Syntax: '"both"') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 4) (Syntax: '4') Next (Regular) Block[B4] @@ -532,8 +565,29 @@ static void Main(bool a) .locals {R1} { Locals: [MyList list2] + CaptureIds: [0] Block[B1] - Block Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'a') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '10') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '20') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 20) (Syntax: '20') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyList, IsImplicit) (Syntax: 'list2 = [wi ... 0 : 20), 2]') Left: @@ -544,13 +598,22 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capac ... 0 : 20), 2]') + CreationArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: a ? 10 : 20') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'a ? 10 : 20') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: name) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... ? 10 : 20)') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "default", IsImplicit) (Syntax: 'with(capaci ... ? 10 : 20)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - Next (Regular) Block[B2] + Next (Regular) Block[B5] Leaving: {R1} } - Block[B2] - Exit - Predecessors: [B1] + Block[B5] - Exit + Predecessors: [B4] Statements (0) """, graph, symbol); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 8b9f7fff9234a..18ee6ab14c30f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1536,7 +1536,19 @@ static void Main() var arrowExpressions = root.DescendantNodes().OfType().ToArray(); var operation1 = semanticModel.GetOperation(arrowExpressions[0]); VerifyOperationTree(compilation, operation1, """ +<<<<<<< Updated upstream +======= + IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(), t]') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(), t]') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(), t]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') + Elements(1): + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') +>>>>>>> Stashed changes """); var operation2 = semanticModel.GetOperation(arrowExpressions[1]); VerifyOperationTree(compilation, operation2, """ @@ -1585,7 +1597,50 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().Single()); VerifyOperationTree(compilation, operation, """ - +<<<<<<< Updated upstream + +======= + IBlockOperation (2 statements, 2 locals) (OperationKind.Block, Type: null) (Syntax: '{ ... }') + Locals: Local_1: System.Collections.Generic.IList x + Local_2: System.Collections.Generic.IList y + IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'IList ... , 1, 2, 3];') + IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'IList ... ), 1, 2, 3]') + Declarators: + IVariableDeclaratorOperation (Symbol: System.Collections.Generic.IList x) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'x = [with(), 1, 2, 3]') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= [with(), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Initializer: + null + IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'IList ... , 1, 2, 3];') + IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'IList ... ), 1, 2, 3]') + Declarators: + IVariableDeclaratorOperation (Symbol: System.Collections.Generic.IList y) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'y = [with(c ... ), 1, 2, 3]') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= [with(cap ... ), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(capac ... ), 1, 2, 3]') + CreationArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 6') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 6) (Syntax: '6') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Initializer: + null +>>>>>>> Stashed changes """); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); From f781408953f883c35b7bd158b08804fd60d25795 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 11:40:22 -0800 Subject: [PATCH 04/92] Fix --- .../CollectionExpressionTests_WithElement_Extra.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 18ee6ab14c30f..905c61120edbc 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1536,9 +1536,6 @@ static void Main() var arrowExpressions = root.DescendantNodes().OfType().ToArray(); var operation1 = semanticModel.GetOperation(arrowExpressions[0]); VerifyOperationTree(compilation, operation1, """ -<<<<<<< Updated upstream - -======= IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(), t]') IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(), t]') ReturnedValue: @@ -1548,7 +1545,6 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') Elements(1): IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') ->>>>>>> Stashed changes """); var operation2 = semanticModel.GetOperation(arrowExpressions[1]); VerifyOperationTree(compilation, operation2, """ @@ -1597,9 +1593,6 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().Single()); VerifyOperationTree(compilation, operation, """ -<<<<<<< Updated upstream - -======= IBlockOperation (2 statements, 2 locals) (OperationKind.Block, Type: null) (Syntax: '{ ... }') Locals: Local_1: System.Collections.Generic.IList x Local_2: System.Collections.Generic.IList y @@ -1640,7 +1633,6 @@ static void Main() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') Initializer: null ->>>>>>> Stashed changes """); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); From f489a02c7654cb0b0b166d80c54b53fa062f7db8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 11:40:50 -0800 Subject: [PATCH 05/92] Cleanup --- .../Semantics/CollectionExpressionTests_WithElement_Extra.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 905c61120edbc..46c4c11cbb231 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1558,7 +1558,6 @@ static void Main() Elements(1): IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') """); - } [Fact] From 4fb9888e2855683356ef4ad8cf044ba7612bb959 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 11:41:01 -0800 Subject: [PATCH 06/92] Cleanup --- .../CollectionExpressionTests_WithElement_Constructor.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs index e63629f0ce36a..3c0080bf2ec5d 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs @@ -392,9 +392,6 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().ToArray()[1]); VerifyOperationTree(compilation, operation, """ -<<<<<<< Updated upstream - -======= ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') CreationArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 10') @@ -407,7 +404,6 @@ static void Main() OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ->>>>>>> Stashed changes """); } From c5f3662a7ed345517cfd4f77cf5261f54b1f77c7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 11:45:36 -0800 Subject: [PATCH 07/92] Fixup test --- ...ectionExpressionTests_WithElement_Extra.cs | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 46c4c11cbb231..502a1971bdab5 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1549,14 +1549,19 @@ static void Main() var operation2 = semanticModel.GetOperation(arrowExpressions[1]); VerifyOperationTree(compilation, operation2, """ IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(t), t]') - IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(t), t]') - ReturnedValue: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(t), t]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(T arg, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(t), t]') - Elements(1): - IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(t), t]') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(t), t]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(T arg, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(t), t]') + CreationArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 't') + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') """); } @@ -1668,6 +1673,11 @@ static void Main() (CollectionExpression) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(capac ... ), 1, 2, 3]') + CreationArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 6') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 6) (Syntax: '6') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') From e431b33ece3a5e457f3ebd23586430e8c31a3c36 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 12:34:19 -0800 Subject: [PATCH 08/92] Remove prototype --- .../CollectionExpressionTests_WithElement_Constructor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs index 3c0080bf2ec5d..2246f6856d561 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs @@ -550,8 +550,6 @@ static void Main(bool a) var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Single()); var root = semanticModel.SyntaxTree.GetRoot(); - // PROTOTYPE: Update IOp to represent with(...) creation. - var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().ToArray()[1], semanticModel); ControlFlowGraphVerifier.VerifyGraph(compilation, """ Block[B0] - Entry From d6078c559ea62f674717e74bf3d70b0e280174bb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 5 Nov 2025 15:05:15 -0800 Subject: [PATCH 09/92] Pass along error bit --- src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs | 3 ++- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index ec0b11fb67e19..f1de34f4fab9f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1366,7 +1366,8 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( argsToParamsOpt: argsToParams, defaultArguments: projectionCall.DefaultArguments, resultKind: LookupResultKind.Viable, - type: collectionBuilderMethod.ReturnType) + type: collectionBuilderMethod.ReturnType, + hasErrors: projectionCall.HasErrors) { WasCompilerGenerated = @this._node.WithElement is null, }; diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index ba5d3c973fbc0..eaf1fe944a9de 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1267,6 +1267,9 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl ImmutableArray getCreationArguments() { var collectionCreation = expr.CollectionCreation; + if (collectionCreation is { HasAnyErrors: true }) + return []; + while (collectionCreation is BoundConversion conversion) collectionCreation = conversion.Operand; From d3946ca31e3829ba80670247f49156433637baba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 10:57:09 -0800 Subject: [PATCH 10/92] Update test --- .../Semantics/CollectionExpressionTests_WithElement_Extra.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 502a1971bdab5..e2ca354c029b7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -3973,10 +3973,7 @@ static void Main() comp.VerifyEmitDiagnostics( // (12,69): error CS9405: No overload for method 'Create' takes 1 'with(...)' element arguments // static IMyCollection F(ReadOnlySpan items, T arg) => [with(arg), ..items]; - Diagnostic(ErrorCode.ERR_BadCollectionArgumentsArgCount, "with(arg)").WithArguments("Create", "1").WithLocation(12, 69), - // (12,74): warning CS8620: Argument of type 'T' cannot be used for parameter 'items' of type 'ReadOnlySpan' in 'MyCollection MyCollectionBuilder.Create(ReadOnlySpan items)' due to differences in the nullability of reference types. - // static IMyCollection F(ReadOnlySpan items, T arg) => [with(arg), ..items]; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "arg").WithArguments("T", "System.ReadOnlySpan", "items", "MyCollection MyCollectionBuilder.Create(ReadOnlySpan items)").WithLocation(12, 74)); + Diagnostic(ErrorCode.ERR_BadCollectionArgumentsArgCount, "with(arg)").WithArguments("Create", "1").WithLocation(12, 69)); } [Fact] From 51b41e2a36e2ec9186c08d1983ab02fdc6dcb444 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 13:52:59 -0800 Subject: [PATCH 11/92] Assert syntax --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 9 ++++++++- .../Core/Portable/Operations/PlaceholderKind.cs | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index eaf1fe944a9de..d3c2203c1277e 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -324,8 +324,15 @@ public CSharpOperationFactory(SemanticModel semanticModel) }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.ValuePlaceholder: + // The only supported use of BoundValuePlaceholder is within a collection expression as we use it + // to represents the elements passed to the collection builder creation methods. We can hit these + // creation methods when producing the .CreationArguments for the ICollectionExpressionOperation. + // Note: the caller will end up stripping this off when producing the CreationArguments, so it will + // not actually leak to the user. But this ends up keeping the logic simple between that callsite + // and this code which actually hits all the arguments passed along. + Debug.Assert(boundNode.Syntax is CollectionExpressionSyntax); return new PlaceholderOperation( - PlaceholderKind.Unspecified, _semanticModel, boundNode.Syntax, + PlaceholderKind.CollectionExpressionElements, _semanticModel, boundNode.Syntax, boundNode switch { BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), diff --git a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs index 42d5582bfd647..ccd1af9256c81 100644 --- a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs +++ b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs @@ -11,5 +11,6 @@ internal enum PlaceholderKind ForToLoopBinaryOperatorLeftOperand = 2, ForToLoopBinaryOperatorRightOperand = 3, AggregationGroup = 4, + CollectionExpressionElements = 5, } } From d4da134e8c08d9cc64e0761186e3d7152bf607ce Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 13:54:21 -0800 Subject: [PATCH 12/92] Add assert --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index d3c2203c1277e..c0b31f84c2a14 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1288,6 +1288,8 @@ ImmutableArray getCreationArguments() // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. var arguments = DeriveArguments(collectionCreation); + + Debug.Assert(arguments.Length > 0, "We should always have at least one argument (the placeholder elements)."); return arguments is [.. var normalArguments, _] ? normalArguments : arguments; From 5d69b09d5fff63642548b248248840269fe87eed Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 13:55:40 -0800 Subject: [PATCH 13/92] Make local functino static --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index c0b31f84c2a14..7b8d6a2f8b2a4 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1245,7 +1245,7 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl ImmutableArray elements = expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr); return new CollectionExpressionOperation( constructMethod, - getCreationArguments(), + getCreationArguments(this, expr), elements, _semanticModel, syntax, @@ -1271,7 +1271,8 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl } } - ImmutableArray getCreationArguments() + static ImmutableArray getCreationArguments( + CSharpOperationFactory @this, BoundCollectionExpression expr) { var collectionCreation = expr.CollectionCreation; if (collectionCreation is { HasAnyErrors: true }) @@ -1281,13 +1282,13 @@ ImmutableArray getCreationArguments() collectionCreation = conversion.Operand; if (collectionCreation is BoundObjectCreationExpression) - return DeriveArguments(collectionCreation); + return @this.DeriveArguments(collectionCreation); if (collectionCreation is BoundCall) { // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. - var arguments = DeriveArguments(collectionCreation); + var arguments = @this.DeriveArguments(collectionCreation); Debug.Assert(arguments.Length > 0, "We should always have at least one argument (the placeholder elements)."); return arguments is [.. var normalArguments, _] From 2860c921629a08726bee092c404447fb29d64597 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 13:59:09 -0800 Subject: [PATCH 14/92] Refine assert --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 7b8d6a2f8b2a4..1587831fec426 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -330,7 +330,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) // Note: the caller will end up stripping this off when producing the CreationArguments, so it will // not actually leak to the user. But this ends up keeping the logic simple between that callsite // and this code which actually hits all the arguments passed along. - Debug.Assert(boundNode.Syntax is CollectionExpressionSyntax); + Debug.Assert(boundNode.Syntax is CollectionExpressionSyntax or WithElementSyntax); return new PlaceholderOperation( PlaceholderKind.CollectionExpressionElements, _semanticModel, boundNode.Syntax, boundNode switch From 9ad9bff6cb27a665b4babb212dfecbd737291856 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 14:05:19 -0800 Subject: [PATCH 15/92] Remove specialized kind --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 5 ++++- src/Compilers/Core/Portable/Operations/PlaceholderKind.cs | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 1587831fec426..2cb8a9aa1c176 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -331,8 +331,11 @@ public CSharpOperationFactory(SemanticModel semanticModel) // not actually leak to the user. But this ends up keeping the logic simple between that callsite // and this code which actually hits all the arguments passed along. Debug.Assert(boundNode.Syntax is CollectionExpressionSyntax or WithElementSyntax); + + // Because we never actually expose this placeholder in the IOp tree, it's fine to use .Unspecified + // as its kind here. return new PlaceholderOperation( - PlaceholderKind.CollectionExpressionElements, _semanticModel, boundNode.Syntax, + PlaceholderKind.Unspecified, _semanticModel, boundNode.Syntax, boundNode switch { BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), diff --git a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs index ccd1af9256c81..42d5582bfd647 100644 --- a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs +++ b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs @@ -11,6 +11,5 @@ internal enum PlaceholderKind ForToLoopBinaryOperatorLeftOperand = 2, ForToLoopBinaryOperatorRightOperand = 3, AggregationGroup = 4, - CollectionExpressionElements = 5, } } From 912a038883fc507007f5ff92b09ebcbe361fb3eb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 16:59:28 -0800 Subject: [PATCH 16/92] Update src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs Co-authored-by: Rikki Gibson --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 2cb8a9aa1c176..e2445cff95f69 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -325,7 +325,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.ValuePlaceholder: // The only supported use of BoundValuePlaceholder is within a collection expression as we use it - // to represents the elements passed to the collection builder creation methods. We can hit these + // to represent the elements passed to the collection builder creation methods. We can hit these // creation methods when producing the .CreationArguments for the ICollectionExpressionOperation. // Note: the caller will end up stripping this off when producing the CreationArguments, so it will // not actually leak to the user. But this ends up keeping the logic simple between that callsite From a63fa85f3684ae8d249c07be64a588cb4de93d4e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 6 Nov 2025 17:38:12 -0800 Subject: [PATCH 17/92] Simplify --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index e2445cff95f69..b4a3abf5da923 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1278,9 +1278,6 @@ static ImmutableArray getCreationArguments( CSharpOperationFactory @this, BoundCollectionExpression expr) { var collectionCreation = expr.CollectionCreation; - if (collectionCreation is { HasAnyErrors: true }) - return []; - while (collectionCreation is BoundConversion conversion) collectionCreation = conversion.Operand; From a5f83e78e392f64b62b104925acf842981a88acc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 09:52:05 -0800 Subject: [PATCH 18/92] Tighten assert --- .../Operations/CSharpOperationFactory.cs | 16 +++++++--------- .../Core/Compilation/CompilationExtensions.cs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index b4a3abf5da923..62f59ba743348 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Operations { @@ -324,16 +323,15 @@ public CSharpOperationFactory(SemanticModel semanticModel) }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.ValuePlaceholder: - // The only supported use of BoundValuePlaceholder is within a collection expression as we use it - // to represent the elements passed to the collection builder creation methods. We can hit these + // The only supported use of BoundValuePlaceholder is within a collection expression as we use it to + // represent the elements passed to the collection builder creation methods. We can hit these // creation methods when producing the .CreationArguments for the ICollectionExpressionOperation. // Note: the caller will end up stripping this off when producing the CreationArguments, so it will // not actually leak to the user. But this ends up keeping the logic simple between that callsite - // and this code which actually hits all the arguments passed along. - Debug.Assert(boundNode.Syntax is CollectionExpressionSyntax or WithElementSyntax); - - // Because we never actually expose this placeholder in the IOp tree, it's fine to use .Unspecified - // as its kind here. + // and this code which actually hits all the arguments passed along. Because we never actually + // expose this placeholder in the IOp tree, it's fine to use .Unspecified as its kind here. + // + // See the logic in CreateBoundCollectionExpression.getCreationArguments for more info. return new PlaceholderOperation( PlaceholderKind.Unspecified, _semanticModel, boundNode.Syntax, boundNode switch @@ -1290,7 +1288,7 @@ static ImmutableArray getCreationArguments( // We do *not* want to include that information in the Arguments we return. var arguments = @this.DeriveArguments(collectionCreation); - Debug.Assert(arguments.Length > 0, "We should always have at least one argument (the placeholder elements)."); + Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], "We should always have at least one argument (the placeholder elements)."); return arguments is [.. var normalArguments, _] ? normalArguments : arguments; diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index d5874d984e045..164a58753264c 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -4,7 +4,7 @@ #nullable disable // Uncomment to enable the IOperation test hook on all test runs. Do not commit this uncommented. -//#define ROSLYN_TEST_IOPERATION +#define ROSLYN_TEST_IOPERATION // Uncomment to enable the Used Assemblies test hook on all test runs. Do not commit this uncommented. //#define ROSLYN_TEST_USEDASSEMBLIES From 3374363f0d6a3e22e599e811170763a7e13cc6c3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:10:44 -0800 Subject: [PATCH 19/92] Pass along correct info --- src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs | 2 +- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index f1de34f4fab9f..e720f737790f2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1365,7 +1365,7 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( invokedAsExtensionMethod: false, argsToParamsOpt: argsToParams, defaultArguments: projectionCall.DefaultArguments, - resultKind: LookupResultKind.Viable, + resultKind: projectionCall.ResultKind, type: collectionBuilderMethod.ReturnType, hasErrors: projectionCall.HasErrors) { diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 62f59ba743348..465f2fc460fd4 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1284,10 +1284,12 @@ static ImmutableArray getCreationArguments( if (collectionCreation is BoundCall) { + // Handle this call recursively. + if (@this.Create(collectionCreation) is not IInvocationOperation { Arguments: var arguments }) + return []; + // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. - var arguments = @this.DeriveArguments(collectionCreation); - Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], "We should always have at least one argument (the placeholder elements)."); return arguments is [.. var normalArguments, _] ? normalArguments From 0bf76711f51111a4d983547637bacc98365b0965 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:11:59 -0800 Subject: [PATCH 20/92] Update src/Compilers/Core/Portable/Operations/OperationInterfaces.xml Co-authored-by: Rikki Gibson --- src/Compilers/Core/Portable/Operations/OperationInterfaces.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 7cb1acf00aff7..4f910995a027e 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3702,7 +3702,7 @@ Arguments passed to a with(...) element on the collection expression, if any. If the collection expression does not have a with(...) element, or does not allow - any arguments, this can be an empty array. + any arguments, this can be an empty array. Will never be null. From 801968e9feb961e4fa9e59f11cb1473ebc77a51a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:12:21 -0800 Subject: [PATCH 21/92] Update src/Compilers/Test/Core/Compilation/CompilationExtensions.cs --- src/Compilers/Test/Core/Compilation/CompilationExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index 164a58753264c..d5874d984e045 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -4,7 +4,7 @@ #nullable disable // Uncomment to enable the IOperation test hook on all test runs. Do not commit this uncommented. -#define ROSLYN_TEST_IOPERATION +//#define ROSLYN_TEST_IOPERATION // Uncomment to enable the Used Assemblies test hook on all test runs. Do not commit this uncommented. //#define ROSLYN_TEST_USEDASSEMBLIES From b7f723f52be375b29522cfdfa51b61f865729afd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:15:57 -0800 Subject: [PATCH 22/92] Rename --- .../Operations/CSharpOperationFactory.cs | 8 +++---- .../Generated/Operations.Generated.cs | 22 +++++++++---------- .../Operations/OperationInterfaces.xml | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 465f2fc460fd4..42d31bf90a5b0 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1282,11 +1282,11 @@ static ImmutableArray getCreationArguments( if (collectionCreation is BoundObjectCreationExpression) return @this.DeriveArguments(collectionCreation); - if (collectionCreation is BoundCall) + // Match the logic in CreateBoundCallOperation which does not DeriveArguments in the case of an + // erroneous call node. + if (collectionCreation is BoundCall { IsErroneousNode: false }) { - // Handle this call recursively. - if (@this.Create(collectionCreation) is not IInvocationOperation { Arguments: var arguments }) - return []; + var arguments = @this.DeriveArguments(collectionCreation); // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 26b49df9067c7..02fd701bfc404 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3959,7 +3959,7 @@ public interface ICollectionExpressionOperation : IOperation /// If the collection expression does not have a with(...) element, or does not allow /// any arguments, this can be an empty array. /// - ImmutableArray CreationArguments { get; } + ImmutableArray ConstructArguments { get; } /// /// Collection expression elements. /// @@ -10678,25 +10678,25 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i } internal sealed partial class CollectionExpressionOperation : Operation, ICollectionExpressionOperation { - internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray creationArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray constructArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) : base(semanticModel, syntax, isImplicit) { ConstructMethod = constructMethod; - CreationArguments = SetParentOperation(creationArguments, this); + ConstructArguments = SetParentOperation(constructArguments, this); Elements = SetParentOperation(elements, this); Type = type; } public IMethodSymbol? ConstructMethod { get; } - public ImmutableArray CreationArguments { get; } + public ImmutableArray ConstructArguments { get; } public ImmutableArray Elements { get; } internal override int ChildOperationsCount => - CreationArguments.Length + + ConstructArguments.Length + Elements.Length; internal override IOperation GetCurrent(int slot, int index) => slot switch { - 0 when index < CreationArguments.Length - => CreationArguments[index], + 0 when index < ConstructArguments.Length + => ConstructArguments[index], 1 when index < Elements.Length => Elements[index], _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), @@ -10706,9 +10706,9 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previ switch (previousSlot) { case -1: - if (!CreationArguments.IsEmpty) return (true, 0, 0); + if (!ConstructArguments.IsEmpty) return (true, 0, 0); else goto case 0; - case 0 when previousIndex + 1 < CreationArguments.Length: + case 0 when previousIndex + 1 < ConstructArguments.Length: return (true, 0, previousIndex + 1); case 0: if (!Elements.IsEmpty) return (true, 1, 0); @@ -10732,7 +10732,7 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i case 1 when previousIndex > 0: return (true, 1, previousIndex - 1); case 1: - if (!CreationArguments.IsEmpty) return (true, 0, CreationArguments.Length - 1); + if (!ConstructArguments.IsEmpty) return (true, 0, ConstructArguments.Length - 1); else goto case 0; case 0 when previousIndex > 0: return (true, 0, previousIndex - 1); @@ -11421,7 +11421,7 @@ public override IOperation VisitInlineArrayAccess(IInlineArrayAccessOperation op public override IOperation VisitCollectionExpression(ICollectionExpressionOperation operation, object? argument) { var internalOperation = (CollectionExpressionOperation)operation; - return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.CreationArguments), VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.ConstructArguments), VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } public override IOperation VisitSpread(ISpreadOperation operation, object? argument) { diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 7cb1acf00aff7..13b2978913f27 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3669,7 +3669,7 @@ - + Represents a collection expression. @@ -3697,7 +3697,7 @@ - + Arguments passed to a with(...) element on the collection expression, if any. From d4e451b5d6e7e76701c26c0576e271db5bf94b9d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:16:30 -0800 Subject: [PATCH 23/92] Update names --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 2 +- src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 9192e50575ef0..c9fc0f2b829c6 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6542,7 +6542,7 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = PushStackFrame(); - var creationArguments = VisitArguments(operation.CreationArguments, instancePushed: false); + var creationArguments = VisitArguments(operation.ConstructArguments, instancePushed: false); PopStackFrame(frame); frame = PushStackFrame(); diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index 88dd697c3511a..6a8049100dec7 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -488,7 +488,7 @@ internal override void VisitFixed(IFixedOperation operation) public override void VisitCollectionExpression(ICollectionExpressionOperation operation) { Assert.Equal(OperationKind.CollectionExpression, operation.Kind); - AssertEx.Equal([.. operation.CreationArguments, .. operation.Elements], operation.ChildOperations); + AssertEx.Equal([.. operation.ConstructArguments, .. operation.Elements], operation.ChildOperations); } public override void VisitSpread(ISpreadOperation operation) From aa06ca1e2207692437bc9ddcd60a1c945e1644c3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 10:24:51 -0800 Subject: [PATCH 24/92] renames --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 4 ++-- .../CollectionExpressionTests_WithElement_Constructor.cs | 8 ++++---- .../CollectionExpressionTests_WithElement_Extra.cs | 6 +++--- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 2 +- .../Test/Core/Compilation/OperationTreeVerifier.cs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 42d31bf90a5b0..1c794d26e92bb 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -325,8 +325,8 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.ValuePlaceholder: // The only supported use of BoundValuePlaceholder is within a collection expression as we use it to // represent the elements passed to the collection builder creation methods. We can hit these - // creation methods when producing the .CreationArguments for the ICollectionExpressionOperation. - // Note: the caller will end up stripping this off when producing the CreationArguments, so it will + // creation methods when producing the .ConstructArguments for the ICollectionExpressionOperation. + // Note: the caller will end up stripping this off when producing the ConstructArguments, so it will // not actually leak to the user. But this ends up keeping the logic simple between that callsite // and this code which actually hits all the arguments passed along. Because we never actually // expose this placeholder in the IOp tree, it's fine to use .Unspecified as its kind here. diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs index 2246f6856d561..1e88c7a01a9a0 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Constructor.cs @@ -393,7 +393,7 @@ static void Main() var operation = semanticModel.GetOperation(root.DescendantNodes().OfType().ToArray()[1]); VerifyOperationTree(compilation, operation, """ ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') - CreationArguments(2): + ConstructArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 10') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -464,7 +464,7 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capacity: 10), 2]') - CreationArguments(2): + ConstructArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 10') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -486,7 +486,7 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capac ... "both"), 4]') - CreationArguments(2): + ConstructArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 20') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 20) (Syntax: '20') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -592,7 +592,7 @@ static void Main(bool a) (CollectionExpression) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyList..ctor([System.Int32 capacity = 0], [System.String name = "default"])) (OperationKind.CollectionExpression, Type: MyList) (Syntax: '[with(capac ... 0 : 20), 2]') - CreationArguments(2): + ConstructArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: a ? 10 : 20') IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'a ? 10 : 20') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index e2ca354c029b7..ed488dd24df67 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1555,7 +1555,7 @@ static void Main() Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(T arg, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(t), t]') - CreationArguments(1): + ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 't') IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -1626,7 +1626,7 @@ static void Main() Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(capac ... ), 1, 2, 3]') - CreationArguments(1): + ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 6') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 6) (Syntax: '6') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -1673,7 +1673,7 @@ static void Main() (CollectionExpression) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(capac ... ), 1, 2, 3]') - CreationArguments(1): + ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 6') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 6) (Syntax: '6') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index ac999afba3bd3..864c72b539962 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,7 +21,7 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.ReduceExtensionMember(Microsoft.CodeAnalysis.ITypeSymbol! receiverType) -> Microsoft.CodeAnalysis.IPropertySymbol? -Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.CreationArguments.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.ConstructArguments.get -> System.Collections.Immutable.ImmutableArray static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 5c2c86720be13..8d789579b26cd 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1615,8 +1615,8 @@ public override void VisitCollectionExpression(ICollectionExpressionOperation op LogString(")"); LogCommonPropertiesAndNewLine(operation); - if (operation.CreationArguments.Length > 0) - VisitArray(operation.CreationArguments, nameof(operation.CreationArguments), logElementCount: true); + if (operation.ConstructArguments.Length > 0) + VisitArray(operation.ConstructArguments, nameof(operation.ConstructArguments), logElementCount: true); VisitArray(operation.Elements, nameof(operation.Elements), logElementCount: true); } From ab9c2322ae25bee889de92c746c7ddfc67294905 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 14:36:14 -0800 Subject: [PATCH 25/92] Use a more loosely typed array --- Roslyn.slnx | 413 ++++++++++++++++++ .../Operations/CSharpOperationFactory.cs | 17 +- .../Generated/Operations.Generated.cs | 10 +- .../Operations/ControlFlowGraphBuilder.cs | 9 +- .../Operations/OperationInterfaces.xml | 6 +- .../Core/Portable/PublicAPI.Unshipped.txt | 2 +- 6 files changed, 443 insertions(+), 14 deletions(-) create mode 100644 Roslyn.slnx diff --git a/Roslyn.slnx b/Roslyn.slnx new file mode 100644 index 0000000000000..c2085ce471d63 --- /dev/null +++ b/Roslyn.slnx @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 1c794d26e92bb..cbaa25ac64b6e 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1272,7 +1272,7 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl } } - static ImmutableArray getCreationArguments( + static ImmutableArray getCreationArguments( CSharpOperationFactory @this, BoundCollectionExpression expr) { var collectionCreation = expr.CollectionCreation; @@ -1280,20 +1280,25 @@ static ImmutableArray getCreationArguments( collectionCreation = conversion.Operand; if (collectionCreation is BoundObjectCreationExpression) - return @this.DeriveArguments(collectionCreation); + return ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); - // Match the logic in CreateBoundCallOperation which does not DeriveArguments in the case of an - // erroneous call node. - if (collectionCreation is BoundCall { IsErroneousNode: false }) + if (collectionCreation is BoundCall boundCall) { + // Match the logic in CreateBoundCallOperation which does not DeriveArguments in the case of an + // erroneous call node. + if (boundCall.IsErroneousNode) + return @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); + var arguments = @this.DeriveArguments(collectionCreation); // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], "We should always have at least one argument (the placeholder elements)."); - return arguments is [.. var normalArguments, _] + var slicedArguments = arguments is [.. var normalArguments, _] ? normalArguments : arguments; + + return ImmutableArray.CastUp(slicedArguments); } return []; diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 02fd701bfc404..ac19defa440c4 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3957,9 +3957,11 @@ public interface ICollectionExpressionOperation : IOperation /// /// Arguments passed to a with(...) element on the collection expression, if any. /// If the collection expression does not have a with(...) element, or does not allow - /// any arguments, this can be an empty array. + /// any arguments, this can be an empty array. Will never be null. If the with(...) element + /// successfully bound, these will all be ; otherwise, they can be + /// any operation. /// - ImmutableArray ConstructArguments { get; } + ImmutableArray ConstructArguments { get; } /// /// Collection expression elements. /// @@ -10678,7 +10680,7 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i } internal sealed partial class CollectionExpressionOperation : Operation, ICollectionExpressionOperation { - internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray constructArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + internal CollectionExpressionOperation(IMethodSymbol? constructMethod, ImmutableArray constructArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) : base(semanticModel, syntax, isImplicit) { ConstructMethod = constructMethod; @@ -10687,7 +10689,7 @@ internal CollectionExpressionOperation(IMethodSymbol? constructMethod, Immutable Type = type; } public IMethodSymbol? ConstructMethod { get; } - public ImmutableArray ConstructArguments { get; } + public ImmutableArray ConstructArguments { get; } public ImmutableArray Elements { get; } internal override int ChildOperationsCount => ConstructArguments.Length + diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index c9fc0f2b829c6..1ef52e04a46c1 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -6542,7 +6543,13 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = PushStackFrame(); - var creationArguments = VisitArguments(operation.ConstructArguments, instancePushed: false); + + // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to + // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but + // fallback to just visiting the array of expressions if we didn't bind successfully. + var creationArguments = ImmutableCollectionsMarshal.AsArray(operation.ConstructArguments) is IArgumentOperation[] argumentArray + ? ImmutableArray.CastUp(VisitArguments(ImmutableCollectionsMarshal.AsImmutableArray(argumentArray), instancePushed: false)) + : VisitArray(operation.ConstructArguments); PopStackFrame(frame); frame = PushStackFrame(); diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index daf78f10deb40..117546f063d15 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3697,12 +3697,14 @@ - + Arguments passed to a with(...) element on the collection expression, if any. If the collection expression does not have a with(...) element, or does not allow - any arguments, this can be an empty array. Will never be null. + any arguments, this can be an empty array. Will never be null. If the with(...) element + successfully bound, these will all be ; otherwise, they can be + any operation. diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 864c72b539962..225c95dd5919c 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,7 +21,7 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.ReduceExtensionMember(Microsoft.CodeAnalysis.ITypeSymbol! receiverType) -> Microsoft.CodeAnalysis.IPropertySymbol? -Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.ConstructArguments.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.ConstructArguments.get -> System.Collections.Immutable.ImmutableArray static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? From d3146db17176d293f444b62716f5491485738c60 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 15:23:08 -0800 Subject: [PATCH 26/92] Simplify --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 1ef52e04a46c1..4eb19215e2997 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6547,9 +6547,10 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but // fallback to just visiting the array of expressions if we didn't bind successfully. - var creationArguments = ImmutableCollectionsMarshal.AsArray(operation.ConstructArguments) is IArgumentOperation[] argumentArray - ? ImmutableArray.CastUp(VisitArguments(ImmutableCollectionsMarshal.AsImmutableArray(argumentArray), instancePushed: false)) - : VisitArray(operation.ConstructArguments); + var arguments = operation.ConstructArguments.As(); + var creationArguments = arguments.IsDefault + ? VisitArray(operation.ConstructArguments) + : ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)); PopStackFrame(frame); frame = PushStackFrame(); From dabb2616f96d64eb8e64b0bd1363b04f43182691 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 15:24:27 -0800 Subject: [PATCH 27/92] Use pattern --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 4eb19215e2997..f0d5d24eea9c7 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6547,10 +6547,10 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but // fallback to just visiting the array of expressions if we didn't bind successfully. - var arguments = operation.ConstructArguments.As(); - var creationArguments = arguments.IsDefault - ? VisitArray(operation.ConstructArguments) - : ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)); + var creationArguments = operation.ConstructArguments.As() is { IsDefault: false } arguments + ? ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)) + : VisitArray(operation.ConstructArguments); + PopStackFrame(frame); frame = PushStackFrame(); From a5db15502fe3de506f5f56ce2cc9f22869480828 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 15:29:51 -0800 Subject: [PATCH 28/92] Add checks --- .../Portable/Operations/CSharpOperationFactory.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index cbaa25ac64b6e..a15b0f2bb82f0 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -714,6 +714,9 @@ private IAnonymousObjectCreationOperation CreateBoundAnonymousObjectCreationExpr return new AnonymousObjectCreationOperation(initializers, _semanticModel, syntax, type, isImplicit); } + private static bool CanDeriveObjectCreationExpressionArguments(BoundObjectCreationExpression boundObjectCreationExpression) + => boundObjectCreationExpression.ResultKind != LookupResultKind.OverloadResolutionFailure && boundObjectCreationExpression.Constructor.OriginalDefinition is not ErrorMethodSymbol; + private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreationExpression boundObjectCreationExpression) { MethodSymbol constructor = boundObjectCreationExpression.Constructor; @@ -724,7 +727,7 @@ private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreat Debug.Assert(constructor is not null); - if (boundObjectCreationExpression.ResultKind == LookupResultKind.OverloadResolutionFailure || constructor.OriginalDefinition is ErrorMethodSymbol) + if (!CanDeriveObjectCreationExpressionArguments(boundObjectCreationExpression)) { var children = CreateFromArray(((IBoundInvalidNode)boundObjectCreationExpression).InvalidNodeChildren); return new InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit); @@ -1279,8 +1282,16 @@ static ImmutableArray getCreationArguments( while (collectionCreation is BoundConversion conversion) collectionCreation = conversion.Operand; - if (collectionCreation is BoundObjectCreationExpression) + if (collectionCreation is BoundObjectCreationExpression objectCreation) + { + // Match the logic in CreateBoundObjectCreationOperation which does not DeriveArguments in the case of an + // problems encountered in binding. + Debug.Assert(!objectCreation.Type.IsAnonymousType); + if (!CanDeriveObjectCreationExpressionArguments(objectCreation)) + return @this.CreateFromArray(((IBoundInvalidNode)objectCreation).InvalidNodeChildren); + return ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); + } if (collectionCreation is BoundCall boundCall) { From 010c783aacc512b61ac9af00ee758818bef0c52b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 18:05:11 -0800 Subject: [PATCH 29/92] remove --- Roslyn.slnx | 413 ---------------------------------------------------- 1 file changed, 413 deletions(-) delete mode 100644 Roslyn.slnx diff --git a/Roslyn.slnx b/Roslyn.slnx deleted file mode 100644 index c2085ce471d63..0000000000000 --- a/Roslyn.slnx +++ /dev/null @@ -1,413 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 3002bcb496668b8a2137d558d98dc2d8647baded Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 7 Nov 2025 18:05:51 -0800 Subject: [PATCH 30/92] Update src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt --- .../ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 5b3c06fd5eebd..a320566785402 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -2014,7 +2014,7 @@ Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_Arg Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_IsDynamic Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructMethod -Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_CreationArguments +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructArguments Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_Elements Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation.get_ConstrainedToType From 0161bfb542b5893b7482cbb4523d35affad28a19 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 8 Nov 2025 00:21:35 -0800 Subject: [PATCH 31/92] reorder --- .../ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 5b3c06fd5eebd..f03759ccffdf8 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -2013,8 +2013,8 @@ Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_Add Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_Arguments Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_IsDynamic Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation +Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructArguments Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructMethod -Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_CreationArguments Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_Elements Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation.get_ConstrainedToType From 26f6f9544673599f1bf150826c51ca9bab2b6853 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Nov 2025 22:16:45 +0100 Subject: [PATCH 32/92] Update docs --- src/Compilers/Core/Portable/Generated/Operations.Generated.cs | 2 +- src/Compilers/Core/Portable/Operations/OperationInterfaces.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index ac19defa440c4..7c0896790a92f 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3957,7 +3957,7 @@ public interface ICollectionExpressionOperation : IOperation /// /// Arguments passed to a with(...) element on the collection expression, if any. /// If the collection expression does not have a with(...) element, or does not allow - /// any arguments, this can be an empty array. Will never be null. If the with(...) element + /// any arguments, this can be an empty array. Will never be default. If the with(...) element /// successfully bound, these will all be ; otherwise, they can be /// any operation. /// diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 117546f063d15..e1d403ae5e1d0 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3702,7 +3702,7 @@ Arguments passed to a with(...) element on the collection expression, if any. If the collection expression does not have a with(...) element, or does not allow - any arguments, this can be an empty array. Will never be null. If the with(...) element + any arguments, this can be an empty array. Will never be default. If the with(...) element successfully bound, these will all be ; otherwise, they can be any operation. From ad0a637329a7ac3567a141fecf6e4dcb28bac1ab Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Nov 2025 22:18:35 +0100 Subject: [PATCH 33/92] Move local --- .../Portable/Operations/CSharpOperationFactory.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index a15b0f2bb82f0..f732a5f6fbf82 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -719,14 +719,11 @@ private static bool CanDeriveObjectCreationExpressionArguments(BoundObjectCreati private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreationExpression boundObjectCreationExpression) { - MethodSymbol constructor = boundObjectCreationExpression.Constructor; SyntaxNode syntax = boundObjectCreationExpression.Syntax; ITypeSymbol? type = boundObjectCreationExpression.GetPublicTypeSymbol(); ConstantValue? constantValue = boundObjectCreationExpression.ConstantValueOpt; bool isImplicit = boundObjectCreationExpression.WasCompilerGenerated; - Debug.Assert(constructor is not null); - if (!CanDeriveObjectCreationExpressionArguments(boundObjectCreationExpression)) { var children = CreateFromArray(((IBoundInvalidNode)boundObjectCreationExpression).InvalidNodeChildren); @@ -749,7 +746,15 @@ private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreat ImmutableArray arguments = DeriveArguments(boundObjectCreationExpression); IObjectOrCollectionInitializerOperation? initializer = (IObjectOrCollectionInitializerOperation?)Create(boundObjectCreationExpression.InitializerExpressionOpt); - return new ObjectCreationOperation(constructor.GetPublicSymbol(), initializer, arguments, _semanticModel, syntax, type, constantValue, isImplicit); + return new ObjectCreationOperation( + boundObjectCreationExpression.Constructor.GetPublicSymbol(), + initializer, + arguments, + _semanticModel, + syntax, + type, + constantValue, + isImplicit); } private IOperation CreateBoundWithExpressionOperation(BoundWithExpression boundWithExpression) From ca616ed8b6fa59e7914553e67a126830c8105954 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 10 Nov 2025 22:20:28 +0100 Subject: [PATCH 34/92] Rename and inline --- .../Operations/CSharpOperationFactory.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index f732a5f6fbf82..d9480333783d2 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1246,22 +1246,16 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundCollectionExpression expr) { - var compilation = (CSharpCompilation)_semanticModel.Compilation; - SyntaxNode syntax = expr.Syntax; - ITypeSymbol? collectionType = expr.GetPublicTypeSymbol(); - bool isImplicit = expr.WasCompilerGenerated; - IMethodSymbol? constructMethod = getConstructMethod(compilation, expr).GetPublicSymbol(); - ImmutableArray elements = expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr); return new CollectionExpressionOperation( - constructMethod, - getCreationArguments(this, expr), - elements, + getConstructMethod(expr).GetPublicSymbol(), + getConstructArguments(this, expr), + expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr), _semanticModel, - syntax, - collectionType, - isImplicit); + expr.Syntax, + expr.GetPublicTypeSymbol(), + expr.WasCompilerGenerated); - static MethodSymbol? getConstructMethod(CSharpCompilation compilation, BoundCollectionExpression expr) + static MethodSymbol? getConstructMethod(BoundCollectionExpression expr) { switch (expr.CollectionTypeKind) { @@ -1280,7 +1274,7 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl } } - static ImmutableArray getCreationArguments( + static ImmutableArray getConstructArguments( CSharpOperationFactory @this, BoundCollectionExpression expr) { var collectionCreation = expr.CollectionCreation; From 288f14a427e140ea3b24bdabace33e1620630c67 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 01:53:39 +0100 Subject: [PATCH 35/92] Change frames --- .../Portable/Operations/ControlFlowGraphBuilder.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index f0d5d24eea9c7..a7ba24b508922 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6543,6 +6543,7 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = PushStackFrame(); + EvalStackFrame argumentsFrame = PushStackFrame(); // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but @@ -6551,9 +6552,8 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize ? ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)) : VisitArray(operation.ConstructArguments); - PopStackFrame(frame); + PopStackFrame(argumentsFrame); - frame = PushStackFrame(); var elements = VisitArray( operation.Elements, unwrapper: static (IOperation element) => @@ -6574,15 +6574,15 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize IsImplicit(spread)) : operation; }); - PopStackFrame(frame); - return new CollectionExpressionOperation( + + return PopStackFrame(frame, new CollectionExpressionOperation( operation.ConstructMethod, creationArguments, elements, semanticModel: null, operation.Syntax, operation.Type, - IsImplicit(operation)); + IsImplicit(operation))); } public override IOperation? VisitSpread(ISpreadOperation operation, int? argument) From b0eb162577fce9334914a359d3327291c3fae76f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 13:21:16 +0100 Subject: [PATCH 36/92] Add iop tessts --- ...nExpressionTests_WithElement_IOperation.cs | 630 ++++++++++++++++++ 1 file changed, 630 insertions(+) create mode 100644 src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs new file mode 100644 index 0000000000000..523e450dd7bc5 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs @@ -0,0 +1,630 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +[CompilerTrait(CompilerFeature.CollectionExpressions)] +public sealed class CollectionExpressionTests_WithElement_IOperation : CSharpTestBase +{ + [Fact] + public void TestArray_Empty() + { + string source = """ + class C + { + void M() + { + int[] a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,20): error CS9401: 'with(...)' elements are not supported for type 'int[]' + // int[] a = [with(), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(5, 20)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestArray_SingleArg() + { + string source = """ + class C + { + void M() + { + int[] a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,20): error CS9401: 'with(...)' elements are not supported for type 'int[]' + // int[] a = [with(0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(5, 20)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestArray_NamedArg() + { + string source = """ + class C + { + void M() + { + int[] a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,20): error CS9401: 'with(...)' elements are not supported for type 'int[]' + // int[] a = [with(capacity: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(5, 20)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestSpan_Empty() + { + string source = """ + using System; + class C + { + void M() + { + Span a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,24): error CS9401: 'with(...)' elements are not supported for type 'Span' + // Span a = [with(), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(6, 24)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestSpan_SingleArg() + { + string source = """ + using System; + class C + { + void M() + { + Span a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,24): error CS9401: 'with(...)' elements are not supported for type 'Span' + // Span a = [with(0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(6, 24)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestSpan_NamedArg() + { + string source = """ + using System; + class C + { + void M() + { + Span a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,24): error CS9401: 'with(...)' elements are not supported for type 'Span' + // Span a = [with(capacity: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(6, 24)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + public void TestReadOnlyInterface_Empty(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + public void TestReadOnlyInterface_SingleArg(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,40): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // IEnumerable a = [with(0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with")); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + public void TestReadOnlyInterface_NamedArg(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,40): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // IEnumerable a = [with(capacity: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with")); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("ICollection")] + [InlineData("IList")] + public void TestMutableInterface_Empty(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("ICollection")] + [InlineData("IList")] + public void TestMutableInterface_SingleArg(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("ICollection")] + [InlineData("IList")] + public void TestMutableInterface_NamedArg(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("ICollection")] + [InlineData("IList")] + public void TestMutableInterface_NamedArg_Incorrect(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(unknown: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,36): error CS1739: The best overload for 'List' does not have a parameter named 'unknown' + // ICollection a = [with(unknown: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadNamedArgument, "unknown").WithArguments("List", "unknown")); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Theory] + [InlineData("ICollection")] + [InlineData("IList")] + public void TestMutableInterface_MultipleArgs(string typeName) + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + {{typeName}} a = [with(0, 1), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,31): error CS1729: 'List' does not contain a constructor that takes 2 arguments + // ICollection a = [with(0, 1), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "with").WithArguments("System.Collections.Generic.List", "2")); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0, 1), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_Empty() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_NamedArg() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_SingleArg() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_MultipleArgs() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(0, null), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(0, null), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_MultipleArgs_Named() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(capacity: 0, comparer: null), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_MultipleArgs_Named_OutOfOrder() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(comparer: null, capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(compa ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_TooManyArgs() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(0, null, ""), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,27): error CS1729: 'HashSet' does not contain a constructor that takes 3 arguments + // HashSet a = [with(0, null, ""), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"with(0, null, """")").WithArguments("System.Collections.Generic.HashSet", "3").WithLocation(6, 27)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestObjectCreation_WrongArgType() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() + { + HashSet a = [with(""), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,32): error CS1503: Argument 1: cannot convert from 'string' to 'System.Collections.Generic.IEnumerable' + // HashSet a = [with(""), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadArgType, @"""""").WithArguments("1", "string", "System.Collections.Generic.IEnumerable").WithLocation(6, 32)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(""), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } +} From 643233ad86aaaf86d9c9551ed534c33d88635c27 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 13:28:36 +0100 Subject: [PATCH 37/92] Update tests --- .../Portable/Operations/CSharpOperationFactory.cs | 3 +++ ...ollectionExpressionTests_WithElement_IOperation.cs | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index d9480333783d2..d0c5ee8c93f59 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1311,6 +1311,9 @@ static ImmutableArray getConstructArguments( return ImmutableArray.CastUp(slicedArguments); } + if (collectionCreation is BoundBadExpression boundBad) + return @this.CreateFromArray(boundBad.ChildBoundNodes); + return []; } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs index 523e450dd7bc5..aae9d96730a93 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs @@ -357,6 +357,8 @@ void M() Diagnostic(ErrorCode.ERR_BadNamedArgument, "unknown").WithArguments("List", "unknown")); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -385,6 +387,9 @@ void M() Diagnostic(ErrorCode.ERR_BadCtorArgCount, "with").WithArguments("System.Collections.Generic.List", "2")); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0, 1), 1, 2, 3]') + ConstructArguments(2): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -595,6 +600,10 @@ void M() Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"with(0, null, """")").WithArguments("System.Collections.Generic.HashSet", "3").WithLocation(6, 27)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + ConstructArguments(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -621,6 +630,8 @@ void M() Diagnostic(ErrorCode.ERR_BadArgType, @"""""").WithArguments("1", "string", "System.Collections.Generic.IEnumerable").WithLocation(6, 32)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(""), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') From 783edaeaab42b0cd5322d7de2bdce36c53927aea Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 13:53:23 +0100 Subject: [PATCH 38/92] Update tests --- ...nExpressionTests_WithElement_IOperation.cs | 285 ++++++++++++++++++ 1 file changed, 285 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs index aae9d96730a93..a8bc2a0a3a007 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs @@ -14,6 +14,30 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests; [CompilerTrait(CompilerFeature.CollectionExpressions)] public sealed class CollectionExpressionTests_WithElement_IOperation : CSharpTestBase { + private const string s_collectionBuilderType = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyHashSetBuilder), nameof(MyHashSetBuilder.Create))] + class MyHashSet : IEnumerable + { + public void Add(int item) { } + + IEnumerator IEnumerable.GetEnumerator() => null!; + IEnumerator IEnumerable.GetEnumerator() => null; + } + + class MyHashSetBuilder + { + public static MyHashSet Create(ReadOnlySpan items) => null!; + public static MyHashSet Create(int capacity, ReadOnlySpan items) => null!; + public static MyHashSet Create(IEqualityComparer comparer, ReadOnlySpan items) => null!; + public static MyHashSet Create(int capacity, IEqualityComparer comparer, ReadOnlySpan items) => null!; + } + """; + [Fact] public void TestArray_Empty() { @@ -638,4 +662,265 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } + + [Fact] + public void TestCollectionBuilder_Empty() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_SingleArg() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_SingleArg_Named() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_SingleArg_WrongName() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(unknown: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (5,29): error CS1739: The best overload for 'Create' does not have a parameter named 'unknown' + // MyHashSet a = [with(unknown: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadNamedArgument, "unknown").WithArguments("Create", "unknown").WithLocation(5, 29)); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_SingleArg_WrongType() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(capacity: ""), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (5,39): error CS1503: Argument 1: cannot convert from 'string' to 'int' + // MyHashSet a = [with(capacity: ""), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadArgType, @"""""").WithArguments("1", "string", "int").WithLocation(5, 39)); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_MultipleArgs() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(0, null), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0, null), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_MultipleArgs_Named() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(capacity: 0, comparer: null), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_MultipleArgs_Named_OutOfOrder() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(comparer: null, capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(compa ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestCollectionBuilder_MultipleArgs_TooManyArgs() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(0, null, ""), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (5,24): error CS9405: No overload for method 'Create' takes 3 'with(...)' element arguments + // MyHashSet a = [with(0, null, ""), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_BadCollectionArgumentsArgCount, @"with(0, null, """")").WithArguments("Create", "3").WithLocation(5, 24)); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + ConstructArguments(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } } From 8eb934342b1104bbf1b7ecdba5d4f045a0ce1bfb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 14:27:04 +0100 Subject: [PATCH 39/92] Update tests --- .../Portable/Binder/Binder_Conversions.cs | 17 +++-- ...ressionTests_WithElement_ArraysAndSpans.cs | 15 +--- ...ectionExpressionTests_WithElement_Extra.cs | 71 +++++++++++-------- ...nExpressionTests_WithElement_IOperation.cs | 28 +++++--- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index e720f737790f2..a22f4cbdae4d7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1147,6 +1147,7 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( ErrorCode.ERR_CollectionArgumentsNotSupportedForType, _node.WithElement.Syntax.GetFirstToken().GetLocation(), _targetType); + return null; } return CreateCollectionExpression(collectionTypeKind, elements); @@ -1154,6 +1155,15 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( private readonly BoundCollectionExpression? TryConvertCollectionExpressionArrayInterfaceType(ImmutableArray elements) { + if (_node.WithElement?.Arguments.Length > 0 && + _targetType.IsReadOnlyArrayInterface(out _)) + { + // For the read-only array interfaces (IEnumerable, IReadOnlyCollection, IReadOnlyList), only + // the parameterless `with()` is allowed. + _diagnostics.Add(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, _node.WithElement.Syntax.GetFirstToken().GetLocation()); + return null; + } + return CreateCollectionExpression( CollectionExpressionTypeKind.ArrayInterface, elements, @@ -1170,13 +1180,6 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( var withSyntax = withElement.Syntax; if (@this._targetType.IsReadOnlyArrayInterface(out _)) { - // For the read-only array interfaces (IEnumerable, IReadOnlyCollection, IReadOnlyList), only - // the parameterless `with()` is allowed. - if (withElement.Arguments.Length > 0) - { - @this._diagnostics.Add(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, withSyntax.GetFirstToken().GetLocation()); - } - // Note: we intentionally report null here. Even though the code has `with()` in it, we're not actually // going to call a particular constructor. The lowering phase will properly handle creating a read-only // interface instance. diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs index f57bd6b6f41af..5189d1667c2ef 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs @@ -518,10 +518,7 @@ Span Method() """; CreateCompilation(source, targetFramework: TargetFramework.Net80).VerifyDiagnostics( - // (7,16): error CS9203: A collection expression of type 'Span' cannot be used in this context because it may be exposed outside of the current scope. - // return [with(), 1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[with(), 1, 2, 3]").WithArguments("System.Span").WithLocation(7, 16), - // (7,17): error CS9336: Collection arguments are not supported for type 'Span'. + // (7,17): error CS9401: 'with(...)' elements are not supported for type 'Span' // return [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(7, 17)); } @@ -562,10 +559,7 @@ void M() """; CreateCompilation(source).VerifyDiagnostics( - // (5,13): warning CS0219: The variable 'x' is assigned but its value is never used - // int x = 10; - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(5, 13), - // (6,24): error CS9336: Collection arguments are not supported for type 'int[]'. + // (6,24): error CS9401: 'with(...)' elements are not supported for type 'int[]' // int[] array = [with(ref x), 1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(6, 24)); } @@ -587,10 +581,7 @@ void M() """; CreateCompilation(source, targetFramework: TargetFramework.Net80).VerifyDiagnostics( - // (7,13): warning CS0219: The variable 'x' is assigned but its value is never used - // int x = 10; - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(7, 13), - // (8,27): error CS9336: Collection arguments are not supported for type 'Span'. + // (8,27): error CS9401: 'with(...)' elements are not supported for type 'Span' // Span span = [with(in x), 1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(8, 27)); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index aaa3ef11376d9..265000c48babe 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -244,10 +244,13 @@ static void F(T t) """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,14): error CS9336: Collection arguments are not supported for type 'T[]'. + // (6,14): error CS9401: 'with(...)' elements are not supported for type 'T[]' // a = [with(default), t]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("T[]").WithLocation(6, 14), - // (7,17): error CS9335: Collection argument element must be the first element. + // (6,19): error CS8716: There is no target type for the default literal. + // a = [with(default), t]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 19), + // (7,17): error CS9400: 'with(...)' element must be the first element // a = [t, with(default)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeFirst, "with").WithLocation(7, 17)); @@ -305,8 +308,8 @@ static T[] EmptyArgs() } [Theory] - [InlineData("ReadOnlySpan")] - [InlineData("Span")] + [InlineData("ReadOnlySpan")] + [InlineData("Span")] public void Arguments_Span(string spanType) { string source = $$""" @@ -315,19 +318,22 @@ class Program { static void F(T t) { - {{spanType}} x = + {{spanType}} x = [with(default), t]; - {{spanType}} y = + {{spanType}} y = [t, with(default)]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( - // (7,14): error CS9336: Collection arguments are not supported for type 'Span'. + // (7,14): error CS9401: 'with(...)' elements are not supported for type 'Span' + // [with(default), t]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(spanType).WithLocation(7, 14), + // (7,19): error CS8716: There is no target type for the default literal. // [with(default), t]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments($"System.{spanType}").WithLocation(7, 14), - // (9,17): error CS9335: Collection argument element must be the first element. + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(7, 19), + // (9,17): error CS9400: 'with(...)' element must be the first element // [t, with(default)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeFirst, "with").WithLocation(9, 17)); } @@ -915,19 +921,22 @@ static void Create(int capacity, IEqualityComparer comparer) case "System.ReadOnlySpan": case "System.Span": comp.VerifyEmitDiagnostics( - // (8,14): error CS9336: Collection arguments are not supported for type 'ReadOnlySpan' + // (8,14): error CS9401: 'with(...)' elements are not supported for type 'ReadOnlySpan' // c = [with()]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(typeName).WithLocation(8, 14), - // (9,14): error CS9336: Collection arguments are not supported for type 'ReadOnlySpan' + // (9,14): error CS9401: 'with(...)' elements are not supported for type 'ReadOnlySpan' // c = [with(default)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(typeName).WithLocation(9, 14), - // (10,14): error CS9336: Collection arguments are not supported for type 'ReadOnlySpan' + // (9,19): error CS8716: There is no target type for the default literal. + // c = [with(default)]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(9, 19), + // (10,14): error CS9401: 'with(...)' elements are not supported for type 'ReadOnlySpan' // c = [with(capacity)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(typeName).WithLocation(10, 14), - // (11,14): error CS9336: Collection arguments are not supported for type 'ReadOnlySpan' + // (11,14): error CS9401: 'with(...)' elements are not supported for type 'ReadOnlySpan' // c = [with(comparer)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(typeName).WithLocation(11, 14), - // (12,14): error CS9336: Collection arguments are not supported for type 'ReadOnlySpan' + // (12,14): error CS9401: 'with(...)' elements are not supported for type 'ReadOnlySpan' // c = [with(capacity, comparer)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(typeName).WithLocation(12, 14)); break; @@ -935,18 +944,21 @@ static void Create(int capacity, IEqualityComparer comparer) case "System.Collections.Generic.IReadOnlyCollection": case "System.Collections.Generic.IReadOnlyList": comp.VerifyEmitDiagnostics( - // (9,14): error CS9338: 'with(...)' element for a read-only interface must be empty if present - // c = [with(default)]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(9, 14), - // (10,14): error CS9338: 'with(...)' element for a read-only interface must be empty if present - // c = [with(capacity)]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(10, 14), - // (11,14): error CS9338: 'with(...)' element for a read-only interface must be empty if present - // c = [with(comparer)]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(11, 14), - // (12,14): error CS9338: 'with(...)' element for a read-only interface must be empty if present - // c = [with(capacity, comparer)]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(12, 14)); + // (9,14): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // c = [with(default)]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(9, 14), + // (9,19): error CS8716: There is no target type for the default literal. + // c = [with(default)]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(9, 19), + // (10,14): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // c = [with(capacity)]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(10, 14), + // (11,14): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // c = [with(comparer)]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(11, 14), + // (12,14): error CS9403: 'with(...)' element for a read-only interface must be empty if present + // c = [with(capacity, comparer)]; + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(12, 14)); break; case "System.Collections.Generic.ICollection": case "System.Collections.Generic.IList": @@ -994,10 +1006,13 @@ static void F(T t) else { comp.VerifyEmitDiagnostics( - // (7,14): error CS9338: 'with(...)' element for a read-only interface must be empty if present + // (7,14): error CS9403: 'with(...)' element for a read-only interface must be empty if present // i = [with(default), t]; Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with").WithLocation(7, 14), - // (8,17): error CS9335: Collection argument element must be the first element + // (7,19): error CS8716: There is no target type for the default literal. + // i = [with(default), t]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(7, 19), + // (8,17): error CS9400: 'with(...)' element must be the first element // i = [t, with(default)]; Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeFirst, "with").WithLocation(8, 17)); } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs index a8bc2a0a3a007..53849f0366c37 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs @@ -81,10 +81,12 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(5, 20)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(0), 1, 2, 3]') - Elements(3): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } @@ -106,10 +108,12 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("int[]").WithLocation(5, 20)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') - Elements(3): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } @@ -158,6 +162,8 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(6, 24)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -184,6 +190,8 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(6, 24)); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -239,6 +247,8 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with")); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -268,6 +278,8 @@ void M() Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeEmpty, "with")); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') From 9b10c180c2fff2ed6f2f10758d18dbebaac41054 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 14:33:38 +0100 Subject: [PATCH 40/92] Update tests --- .../CollectionExpressionTests_WithElement_Extra.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 265000c48babe..116544d519ba9 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -308,12 +308,11 @@ static T[] EmptyArgs() } [Theory] - [InlineData("ReadOnlySpan")] - [InlineData("Span")] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] public void Arguments_Span(string spanType) { string source = $$""" - using System; class Program { static void F(T t) @@ -329,13 +328,13 @@ static void F(T t) comp.VerifyEmitDiagnostics( // (7,14): error CS9401: 'with(...)' elements are not supported for type 'Span' // [with(default), t]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(spanType).WithLocation(7, 14), + Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments(spanType), // (7,19): error CS8716: There is no target type for the default literal. // [with(default), t]; - Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(7, 19), + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 19), // (9,17): error CS9400: 'with(...)' element must be the first element // [t, with(default)]; - Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeFirst, "with").WithLocation(9, 17)); + Diagnostic(ErrorCode.ERR_CollectionArgumentsMustBeFirst, "with").WithLocation(8, 17)); } [Fact] From e1492ceff3153ba3ae8cef98b3097a378d6b63b5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 14:53:36 +0100 Subject: [PATCH 41/92] REvert --- docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md index 296ae016490d2..99e71ac99f08b 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md @@ -170,4 +170,4 @@ object[] items; items = [with(x, y), z]; // C#14: call to with() method; PROTOTYPE C#14: error args not supported for object[] items = [@with(x, y), z]; // call to with() method object with(object a, object b) { ... } -``` +``` \ No newline at end of file From c1f51e49846ddd7a5a32c9a37565ce3a376fd613 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 15:07:07 +0100 Subject: [PATCH 42/92] Handle more cases --- .../Operations/CSharpOperationFactory.cs | 7 ++ ...nExpressionTests_WithElement_IOperation.cs | 79 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index d0c5ee8c93f59..ca9dcdf636b43 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1311,9 +1311,16 @@ static ImmutableArray getConstructArguments( return ImmutableArray.CastUp(slicedArguments); } + if (collectionCreation is BoundNewT boundNewT) + return []; + if (collectionCreation is BoundBadExpression boundBad) return @this.CreateFromArray(boundBad.ChildBoundNodes); + if (collectionCreation is null) + return []; + + Debug.Fail("Unhandled case: " + collectionCreation.GetType()); return []; } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs index 53849f0366c37..1273a9a0ef65a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs @@ -935,4 +935,83 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } + + [Fact] + public void TestTypeArgument_Empty() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() where T : IList, new() + { + T a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestTypeArgument_SingleArg() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() where T : IList, new() + { + T a = [with(0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,16): error CS0417: 'T': cannot provide arguments when creating an instance of a variable type + // T a = [with(0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_NewTyvarWithArgs, "with(0)").WithArguments("T").WithLocation(6, 16)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } + + [Fact] + public void TestTypeArgument_NamedArg() + { + string source = $$""" + using System.Collections.Generic; + class C + { + void M() where T : IList, new() + { + T a = [with(capacity: 0), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (6,16): error CS0417: 'T': cannot provide arguments when creating an instance of a variable type + // T a = [with(capacity: 0), 1, 2, 3]; + Diagnostic(ErrorCode.ERR_NewTyvarWithArgs, "with(capacity: 0)").WithArguments("T").WithLocation(6, 16)); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), $$""" + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); + } } From b4ca300c771594365c602472d1f7c701f767f18a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 15:19:58 +0100 Subject: [PATCH 43/92] Filter out --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 5 ++++- src/Compilers/Test/Core/Compilation/CompilationExtensions.cs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index ca9dcdf636b43..ce56e3f7b57ff 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1297,7 +1297,10 @@ static ImmutableArray getConstructArguments( // Match the logic in CreateBoundCallOperation which does not DeriveArguments in the case of an // erroneous call node. if (boundCall.IsErroneousNode) - return @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); + { + var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); + return array.WhereAsArray(o => o is not IPlaceholderOperation); + } var arguments = @this.DeriveArguments(collectionCreation); diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index d5874d984e045..164a58753264c 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -4,7 +4,7 @@ #nullable disable // Uncomment to enable the IOperation test hook on all test runs. Do not commit this uncommented. -//#define ROSLYN_TEST_IOPERATION +#define ROSLYN_TEST_IOPERATION // Uncomment to enable the Used Assemblies test hook on all test runs. Do not commit this uncommented. //#define ROSLYN_TEST_USEDASSEMBLIES From a3b2e5fa96eeda4487d978150727f87f88d06f0a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 17:20:31 +0100 Subject: [PATCH 44/92] Clean up usings --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index a7ba24b508922..4a5b6cc9714a5 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -9,7 +9,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; From 9f7cce5d8ffba4f49468a5539a5531484fa3c7f9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 17:22:56 +0100 Subject: [PATCH 45/92] Throw if we mix arguments and non-arguments --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 4a5b6cc9714a5..ea3eaa3656566 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6544,6 +6544,9 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize EvalStackFrame frame = PushStackFrame(); EvalStackFrame argumentsFrame = PushStackFrame(); + if (operation.ConstructArguments.Any(a => a is IArgumentOperation) && !operation.ConstructArguments.All(a => a is IArgumentOperation)) + throw ExceptionUtilities.UnexpectedValue("Mixed argument operations and non-argument operations in ConstructArguments"); + // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but // fallback to just visiting the array of expressions if we didn't bind successfully. From 1db3441b30afea4e6647bb47044f82dfc4e53863 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 17:37:43 +0100 Subject: [PATCH 46/92] add test --- ...ectionExpressionTests_WithElement_Extra.cs | 98 +++++++++++++++++++ .../Operations/ControlFlowGraphBuilder.cs | 3 - 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 116544d519ba9..b4e033d420c18 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -7924,4 +7924,102 @@ void N(params List list) { } // N(with(capacity: 0), 1, 2, 3); Diagnostic(ErrorCode.ERR_NameNotInContext, "with").WithArguments("with").WithLocation(6, 11)); } + + [Fact] + public void ControlFlow() + { + var source = """ + using System.Collections.Generic; + class Program + { + static void Main(string[] args) + { + IList y = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + + var verifier = CompileAndVerify(source); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Single()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.IList y] + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') + Left: + ILocalReferenceOperation: y (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(Compu ... seBranch()]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + Next (Regular) Block[B5] + Leaving: {R1} + } + Block[B5] - Exit + Predecessors: [B4] + Statements (0) + """, graph, symbol); + } } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index ea3eaa3656566..77cf7c02fa97a 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6542,7 +6542,6 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = PushStackFrame(); - EvalStackFrame argumentsFrame = PushStackFrame(); if (operation.ConstructArguments.Any(a => a is IArgumentOperation) && !operation.ConstructArguments.All(a => a is IArgumentOperation)) throw ExceptionUtilities.UnexpectedValue("Mixed argument operations and non-argument operations in ConstructArguments"); @@ -6554,8 +6553,6 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize ? ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)) : VisitArray(operation.ConstructArguments); - PopStackFrame(argumentsFrame); - var elements = VisitArray( operation.Elements, unwrapper: static (IOperation element) => From dacba39768be74fd4a3ddb3b5eb927fbaa5b58fe Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 11 Nov 2025 19:50:14 +0100 Subject: [PATCH 47/92] Push all values first before popping --- ...ectionExpressionTests_WithElement_Extra.cs | 22 +++++++++++-------- .../Operations/ControlFlowGraphBuilder.cs | 16 +++++++++++--- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index b4e033d420c18..93c7e0b506d3b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -7952,6 +7952,7 @@ static void Main(string[] args) var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry Statements (0) Next (Regular) Block[B1] @@ -7959,10 +7960,16 @@ static void Main(string[] args) .locals {R1} { Locals: [System.Collections.Generic.IList y] - CaptureIds: [0] + CaptureIds: [0] [1] Block[B1] - Block Predecessors: [B0] - Statements (0) + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') + Value: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) Jump if False (Regular) to Block[B3] IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') Left: @@ -7975,7 +7982,7 @@ static void Main(string[] args) Block[B2] - Block Predecessors: [B1] Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') Value: IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') Instance Receiver: @@ -7985,7 +7992,7 @@ static void Main(string[] args) Block[B3] - Block Predecessors: [B1] Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') Value: IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') Instance Receiver: @@ -8006,14 +8013,11 @@ static void Main(string[] args) ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(Compu ... seBranch()]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') - IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') - Instance Receiver: - null - Arguments(0) + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') Next (Regular) Block[B5] Leaving: {R1} } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 77cf7c02fa97a..add05d5f90c8c 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6549,9 +6549,15 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but // fallback to just visiting the array of expressions if we didn't bind successfully. - var creationArguments = operation.ConstructArguments.As() is { IsDefault: false } arguments - ? ImmutableArray.CastUp(VisitArguments(arguments, instancePushed: false)) - : VisitArray(operation.ConstructArguments); + var arguments = operation.ConstructArguments.As(); + if (arguments.IsDefault) + { + VisitAndPushArray(operation.ConstructArguments); + } + else + { + VisitAndPushArguments(arguments, instancePushed: false); + } var elements = VisitArray( operation.Elements, @@ -6574,6 +6580,10 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize operation; }); + var creationArguments = arguments.IsDefault + ? PopArray(operation.ConstructArguments) + : ImmutableArray.CastUp(PopArray(arguments, RewriteArgumentFromArray)); + return PopStackFrame(frame, new CollectionExpressionOperation( operation.ConstructMethod, creationArguments, From b21f43c3a1b29fafb0555a546e93ab9584ec04a6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 09:13:02 +0100 Subject: [PATCH 48/92] Apply suggestion from @CyrusNajmabadi --- src/Compilers/Test/Core/Compilation/CompilationExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs index 164a58753264c..d5874d984e045 100644 --- a/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs +++ b/src/Compilers/Test/Core/Compilation/CompilationExtensions.cs @@ -4,7 +4,7 @@ #nullable disable // Uncomment to enable the IOperation test hook on all test runs. Do not commit this uncommented. -#define ROSLYN_TEST_IOPERATION +//#define ROSLYN_TEST_IOPERATION // Uncomment to enable the Used Assemblies test hook on all test runs. Do not commit this uncommented. //#define ROSLYN_TEST_USEDASSEMBLIES From a0b96a4047eba5092a3aeccf0a46dc8bbaff2cdf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:05:54 +0100 Subject: [PATCH 49/92] Update comment --- .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index add05d5f90c8c..4ad329af20e15 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6546,9 +6546,9 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize if (operation.ConstructArguments.Any(a => a is IArgumentOperation) && !operation.ConstructArguments.All(a => a is IArgumentOperation)) throw ExceptionUtilities.UnexpectedValue("Mixed argument operations and non-argument operations in ConstructArguments"); - // Ugly, but necessary. If we bound successfully, we'll have an array of IArgumentOperation. We want to - // call through to VisitArguments to handle it properly. So attempt to cast to that type first, but - // fallback to just visiting the array of expressions if we didn't bind successfully. + // If we bound successfully, we'll have an array of IArgumentOperation. We want to call through to + // VisitArguments to handle it properly. So attempt to cast to that type first, but fallback to just + // visiting the array of expressions if we didn't bind successfully. var arguments = operation.ConstructArguments.As(); if (arguments.IsDefault) { From 83c1141548f39d433a21e8f41c1e8ad2091e1ba2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:15:21 +0100 Subject: [PATCH 50/92] Make check more precise --- .../Operations/CSharpOperationFactory.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index ce56e3f7b57ff..f6e2d90c64532 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1299,19 +1299,26 @@ static ImmutableArray getConstructArguments( if (boundCall.IsErroneousNode) { var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); - return array.WhereAsArray(o => o is not IPlaceholderOperation); + Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder] && + placeHolder.Syntax == boundCall.Syntax, "We should always have at least one argument (the placeholder elements)."); + return array is [.. var normalArguments, _] + ? normalArguments + : array; } + else + { + var arguments = @this.DeriveArguments(collectionCreation); - var arguments = @this.DeriveArguments(collectionCreation); - - // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. - // We do *not* want to include that information in the Arguments we return. - Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], "We should always have at least one argument (the placeholder elements)."); - var slicedArguments = arguments is [.. var normalArguments, _] - ? normalArguments - : arguments; + // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. + // We do *not* want to include that information in the Arguments we return. + Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder }] && + placeHolder.Syntax == boundCall.Syntax, "We should always have at least one argument (the placeholder elements)."); + var slicedArguments = arguments is [.. var normalArguments, _] + ? normalArguments + : arguments; - return ImmutableArray.CastUp(slicedArguments); + return ImmutableArray.CastUp(slicedArguments); + } } if (collectionCreation is BoundNewT boundNewT) From 7bdad0d4abef9261e3c0726fcd83f8b80d4466f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:18:52 +0100 Subject: [PATCH 51/92] Add validation --- .../Portable/BoundTree/BoundCollectionExpression.cs | 13 +++++++++++++ .../Portable/Operations/OperationInterfaces.xml | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 32b08d834ec87..d28faa91dc5af 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -16,6 +16,19 @@ private partial void Validate() Debug.Assert(this.CollectionBuilderMethod is not null); Debug.Assert(this.CollectionBuilderElementsPlaceholder is not null); } + +#if DEBUG + var collectionCreation = this.CollectionCreation; + while (collectionCreation is BoundConversion conversion) + collectionCreation = conversion.Operand; + + Debug.Assert(collectionCreation + is null + or BoundObjectCreationExpression + or BoundCall + or BoundNewT + or BoundBadExpression); +#endif } } diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index e1d403ae5e1d0..ad5905d4fc570 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3669,7 +3669,7 @@ - + Represents a collection expression. From 9727a598103a4f639855e63f2e4effbf5ec7200e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:26:28 +0100 Subject: [PATCH 52/92] Validate more --- .../BoundTree/BoundCollectionExpression.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index d28faa91dc5af..c79c4951684ad 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp { @@ -10,13 +11,6 @@ internal partial class BoundCollectionExpression { private partial void Validate() { - if (this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder) - { - Debug.Assert(this.CollectionCreation is not null); - Debug.Assert(this.CollectionBuilderMethod is not null); - Debug.Assert(this.CollectionBuilderElementsPlaceholder is not null); - } - #if DEBUG var collectionCreation = this.CollectionCreation; while (collectionCreation is BoundConversion conversion) @@ -28,6 +22,25 @@ or BoundObjectCreationExpression or BoundCall or BoundNewT or BoundBadExpression); + + if (collectionCreation != null && + this.HasWithElement) + { + Debug.Assert(collectionCreation.Syntax is WithElementSyntax); + } + + if (this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder) + { + Debug.Assert(this.CollectionCreation is not null); + Debug.Assert(this.CollectionBuilderMethod is not null); + Debug.Assert(this.CollectionBuilderElementsPlaceholder is not null); + + Debug.Assert( + collectionCreation is BoundCall + { + Arguments: [.., BoundValuePlaceholder { Kind: BoundKind.ValuePlaceholder } placeHolder], + } boundCall && boundCall.Syntax == placeHolder.Syntax); + } #endif } } From 7ce3cef2156c8556c9d99b3c238d9b6396c56cb5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:33:35 +0100 Subject: [PATCH 53/92] Add iop test --- ...nExpressionTests_WithElement_ArraysAndSpans.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs index 5189d1667c2ef..1c44dd2caca6a 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -580,10 +582,21 @@ void M() } """; - CreateCompilation(source, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + var compilation = CreateCompilation(source, targetFramework: TargetFramework.Net80).VerifyDiagnostics( // (8,27): error CS9401: 'with(...)' elements are not supported for type 'Span' // Span span = [with(in x), 1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, "with").WithArguments("System.Span").WithLocation(8, 27)); + var tree = compilation.SyntaxTrees.Single(); + var root = tree.GetRoot(); + compilation.VerifyOperationTree(root.DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(in x), 1, 2, 3]') + ConstructArguments(1): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + """); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/80518")] From 7a2b9fc40d8bf24155499d23ada061937191fa86 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:42:34 +0100 Subject: [PATCH 54/92] Move tests --- .../IOperationTests_ICollectionExpressionOperation.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Compilers/CSharp/Test/{Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs => IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs} (99%) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs similarity index 99% rename from src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs rename to src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 1273a9a0ef65a..db11922793ace 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_IOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests; [CompilerTrait(CompilerFeature.CollectionExpressions)] -public sealed class CollectionExpressionTests_WithElement_IOperation : CSharpTestBase +public sealed class IOperationTests_ICollectionExpressionOperation : CSharpTestBase { private const string s_collectionBuilderType = """ using System; From 511ca8365301017135b2c885239a8847161d427b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 17:59:23 +0100 Subject: [PATCH 55/92] Add iop tests --- ...ionTests_ICollectionExpressionOperation.cs | 19 ++++++++++++++++++- ...nalysis.CSharp.IOperation.UnitTests.csproj | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index db11922793ace..5f8f1576444b4 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -61,6 +60,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Fact] @@ -88,6 +90,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Fact] @@ -115,6 +120,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Fact] @@ -141,6 +149,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Fact] @@ -169,6 +180,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Fact] @@ -197,6 +211,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Theory] diff --git a/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj b/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj index 197473c8fc89c..a8943af073f74 100644 --- a/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/IOperation/Microsoft.CodeAnalysis.CSharp.IOperation.UnitTests.csproj @@ -19,6 +19,7 @@ + From 1505fdd419ef50084a8f9c2e4b956c02cc9265bf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 13 Nov 2025 18:00:46 +0100 Subject: [PATCH 56/92] Add iop tests --- .../IOperationTests_ICollectionExpressionOperation.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 5f8f1576444b4..9f1fd0e7c2909 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -240,6 +240,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Theory] @@ -271,6 +274,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Theory] @@ -302,6 +308,9 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); + Assert.Null(operation); } [Theory] From 5aa324dfe0eb96b83e50e933270b1844f0d40457 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 14 Nov 2025 15:50:19 +0100 Subject: [PATCH 57/92] remove assert --- .../CSharp/Portable/BoundTree/BoundCollectionExpression.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index c79c4951684ad..872a6b4001192 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -23,12 +23,6 @@ or BoundCall or BoundNewT or BoundBadExpression); - if (collectionCreation != null && - this.HasWithElement) - { - Debug.Assert(collectionCreation.Syntax is WithElementSyntax); - } - if (this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder) { Debug.Assert(this.CollectionCreation is not null); From 1ce3ae2be7e777a0fd96786fdaee2f3cce34b552 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 14 Nov 2025 15:54:20 +0100 Subject: [PATCH 58/92] Update assert --- .../CSharp/Portable/BoundTree/BoundCollectionExpression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 872a6b4001192..0ecb85bc9e826 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -33,7 +33,7 @@ or BoundNewT collectionCreation is BoundCall { Arguments: [.., BoundValuePlaceholder { Kind: BoundKind.ValuePlaceholder } placeHolder], - } boundCall && boundCall.Syntax == placeHolder.Syntax); + } && placeHolder == this.CollectionBuilderElementsPlaceholder); } #endif } From 24b8c4845744dbf34becc4c6ba816c3c2508ed21 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 14 Nov 2025 16:04:39 +0100 Subject: [PATCH 59/92] Move assert --- .../Portable/BoundTree/BoundCollectionExpression.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 0ecb85bc9e826..49c0d158c0722 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -23,17 +23,18 @@ or BoundCall or BoundNewT or BoundBadExpression); + if (collectionCreation is BoundCall boundCall) + { + Debug.Assert( + boundCall.Arguments is [.., BoundValuePlaceholder { Kind: BoundKind.ValuePlaceholder } placeHolder] && + placeHolder == this.CollectionBuilderElementsPlaceholder); + } + if (this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder) { Debug.Assert(this.CollectionCreation is not null); Debug.Assert(this.CollectionBuilderMethod is not null); Debug.Assert(this.CollectionBuilderElementsPlaceholder is not null); - - Debug.Assert( - collectionCreation is BoundCall - { - Arguments: [.., BoundValuePlaceholder { Kind: BoundKind.ValuePlaceholder } placeHolder], - } && placeHolder == this.CollectionBuilderElementsPlaceholder); } #endif } From 759a887130a18e9fded3ac7f6787ffe5f4245d39 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 14 Nov 2025 16:07:56 +0100 Subject: [PATCH 60/92] remove syntax check --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index f6e2d90c64532..172d06e68c0ae 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1299,8 +1299,8 @@ static ImmutableArray getConstructArguments( if (boundCall.IsErroneousNode) { var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); - Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder] && - placeHolder.Syntax == boundCall.Syntax, "We should always have at least one argument (the placeholder elements)."); + Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder], + "We should always have at least one argument (the placeholder elements)."); return array is [.. var normalArguments, _] ? normalArguments : array; From 4190ad6a96e97e75c2f0d6713dc85706c13a7ffc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 14 Nov 2025 16:08:42 +0100 Subject: [PATCH 61/92] remove syntax check --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 172d06e68c0ae..34fd39519fe59 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1299,7 +1299,7 @@ static ImmutableArray getConstructArguments( if (boundCall.IsErroneousNode) { var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); - Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder], + Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified }], "We should always have at least one argument (the placeholder elements)."); return array is [.. var normalArguments, _] ? normalArguments @@ -1311,8 +1311,8 @@ static ImmutableArray getConstructArguments( // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. - Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } placeHolder }] && - placeHolder.Syntax == boundCall.Syntax, "We should always have at least one argument (the placeholder elements)."); + Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], + "We should always have at least one argument (the placeholder elements)."); var slicedArguments = arguments is [.. var normalArguments, _] ? normalArguments : arguments; From 006b6dbad3d295ae64d0c4a1c618b95f71eefc75 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 17 Nov 2025 01:25:44 +0100 Subject: [PATCH 62/92] Introduce a new bound node to make processing collection builder arguments more cleanly. --- .../Portable/Binder/Binder.ValueChecks.cs | 3 + .../Portable/Binder/Binder_Conversions.cs | 6 +- .../BoundTree/BoundCollectionExpression.cs | 2 +- .../Portable/BoundTree/BoundExpression.cs | 5 ++ .../CSharp/Portable/BoundTree/BoundNodes.xml | 9 ++- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 5 ++ .../Portable/FlowAnalysis/NullableWalker.cs | 6 ++ .../Generated/BoundNodes.xml.Generated.cs | 68 +++++++++++++++++-- .../Lowering/LocalRewriter/LocalRewriter.cs | 13 +++- .../Operations/CSharpOperationFactory.cs | 13 ++-- .../Portable/Operations/PlaceholderKind.cs | 1 + 11 files changed, 112 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index d7beb90a6edf1..2ee5450474787 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1190,6 +1190,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin return CheckSimpleAssignmentValueKind(node, assignment, valueKind, diagnostics); case BoundKind.ValuePlaceholder: + case BoundKind.CollectionBuilderElementsPlaceholder: // Strict RValue break; @@ -4361,6 +4362,7 @@ internal SafeContext GetValEscape(BoundExpression expr) case BoundKind.InterpolatedStringArgumentPlaceholder: case BoundKind.AwaitableValuePlaceholder: case BoundKind.ValuePlaceholder: + case BoundKind.CollectionBuilderElementsPlaceholder: return GetPlaceholderScope((BoundValuePlaceholderBase)expr); case BoundKind.Local: @@ -5002,6 +5004,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext case BoundKind.AwaitableValuePlaceholder: case BoundKind.InterpolatedStringArgumentPlaceholder: case BoundKind.ValuePlaceholder: + case BoundKind.CollectionBuilderElementsPlaceholder: if (!GetPlaceholderScope((BoundValuePlaceholderBase)expr).IsConvertibleTo(escapeTo)) { Error(diagnostics, inUnsafeRegion ? ErrorCode.WRN_EscapeVariable : ErrorCode.ERR_EscapeVariable, node, expr.Syntax); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index a22f4cbdae4d7..d322098e42f3b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -835,7 +835,7 @@ private readonly struct CollectionExpressionConverter( private readonly BindingDiagnosticBag _diagnostics = diagnostics; private BoundCollectionExpression CreateCollectionExpression( - CollectionExpressionTypeKind collectionTypeKind, ImmutableArray elements, BoundObjectOrCollectionValuePlaceholder? placeholder = null, BoundExpression? collectionCreation = null, MethodSymbol? collectionBuilderMethod = null, BoundValuePlaceholder? collectionBuilderElementsPlaceholder = null) + CollectionExpressionTypeKind collectionTypeKind, ImmutableArray elements, BoundObjectOrCollectionValuePlaceholder? placeholder = null, BoundExpression? collectionCreation = null, MethodSymbol? collectionBuilderMethod = null, BoundCollectionBuilderElementsPlaceholder? collectionBuilderElementsPlaceholder = null) { return new BoundCollectionExpression( _node.Syntax, @@ -1254,7 +1254,7 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( collectionBuilderMethod: collectionBuilderMethod, collectionBuilderElementsPlaceholder: collectionBuilderElementsPlaceholder); - static (BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundValuePlaceholder? elementsPlaceholder) bindCollectionBuilderInfo( + static (BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundCollectionBuilderElementsPlaceholder? elementsPlaceholder) bindCollectionBuilderInfo( ref readonly CollectionExpressionConverter @this) { var namedType = (NamedTypeSymbol)@this._targetType; @@ -1337,7 +1337,7 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( // CollectionBuilder.Create(a, b, c, ). var readonlySpanParameter = collectionBuilderMethod.Parameters.Last(); - var collectionBuilderElementsPlaceholder = new BoundValuePlaceholder(syntax, readonlySpanParameter.Type) { WasCompilerGenerated = true }; + var collectionBuilderElementsPlaceholder = new BoundCollectionBuilderElementsPlaceholder(syntax, readonlySpanParameter.Type) { WasCompilerGenerated = true }; var arguments = projectionCall.Arguments; var argumentNames = projectionCall.ArgumentNamesOpt; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 49c0d158c0722..242c2d60ec548 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -26,7 +26,7 @@ or BoundNewT if (collectionCreation is BoundCall boundCall) { Debug.Assert( - boundCall.Arguments is [.., BoundValuePlaceholder { Kind: BoundKind.ValuePlaceholder } placeHolder] && + boundCall.Arguments is [.., BoundCollectionBuilderElementsPlaceholder placeHolder] && placeHolder == this.CollectionBuilderElementsPlaceholder); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index b8e6d3ae5a21c..fa331fe528be8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -162,6 +162,11 @@ internal partial class BoundValuePlaceholder public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); } + internal partial class BoundCollectionBuilderElementsPlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable(); + } + internal partial class BoundInterpolatedStringHandlerPlaceholder { public sealed override bool IsEquivalentToThisReference => false; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index bb8f20fe0e9b6..52307e57a1828 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -182,6 +182,10 @@ + + + @@ -1941,7 +1945,7 @@ - + @@ -1959,9 +1963,8 @@ expression types. For example, if the collection is `[with(a, b, c), x, y, z]` initial binding will produce `Factory.Create(a, b, c, )`. The last arg will be replaced in lowering with the ReadOnlySpan for and will naturally be visited during all binding phases. It is just the packaging and movement into the final - and will naturally be visited during all binding phases. It ist just the packaging and movement into the final arg that the lowering phase concerns itself with. --> - + diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 6e456a304b67d..76859e70b8ebe 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3710,6 +3710,11 @@ public override BoundNode VisitValuePlaceholder(BoundValuePlaceholder node) return null; } + public override BoundNode VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) + { + return null; + } + public sealed override BoundNode VisitOutVariablePendingInference(OutVariablePendingInference node) { throw ExceptionUtilities.Unreachable(); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 661bb5bc67ff5..17a46302214a6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -11622,6 +11622,12 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter return null; } + public override BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) + { + VisitPlaceholderWithReplacement(node); + return null; + } + public override BoundNode? VisitEventAccess(BoundEventAccess node) { var updatedSymbol = VisitMemberAccess(node, node.ReceiverOpt, node.EventSymbol); diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 51224a7c3ddf4..3f5b522d46388 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -37,6 +37,7 @@ internal enum BoundKind : byte ListPatternIndexPlaceholder, SlicePatternReceiverPlaceholder, SlicePatternRangePlaceholder, + CollectionBuilderElementsPlaceholder, Dup, PassByCopy, BadExpression, @@ -896,6 +897,34 @@ public BoundSlicePatternRangePlaceholder Update(TypeSymbol type) } } + internal sealed partial class BoundCollectionBuilderElementsPlaceholder : BoundValuePlaceholderBase + { + public BoundCollectionBuilderElementsPlaceholder(SyntaxNode syntax, TypeSymbol? type, bool hasErrors) + : base(BoundKind.CollectionBuilderElementsPlaceholder, syntax, type, hasErrors) + { + } + + public BoundCollectionBuilderElementsPlaceholder(SyntaxNode syntax, TypeSymbol? type) + : base(BoundKind.CollectionBuilderElementsPlaceholder, syntax, type) + { + } + + + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitCollectionBuilderElementsPlaceholder(this); + + public BoundCollectionBuilderElementsPlaceholder Update(TypeSymbol? type) + { + if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundCollectionBuilderElementsPlaceholder(this.Syntax, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + internal sealed partial class BoundDup : BoundExpression { public BoundDup(SyntaxNode syntax, RefKind refKind, TypeSymbol? type, bool hasErrors) @@ -6477,7 +6506,7 @@ public BoundUnconvertedWithElement Update(ImmutableArray argume internal sealed partial class BoundCollectionExpression : BoundCollectionExpressionBase { - public BoundCollectionExpression(SyntaxNode syntax, CollectionExpressionTypeKind collectionTypeKind, BoundObjectOrCollectionValuePlaceholder? placeholder, BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundValuePlaceholder? collectionBuilderElementsPlaceholder, bool wasTargetTyped, bool hasWithElement, BoundUnconvertedCollectionExpression unconvertedCollectionExpression, ImmutableArray elements, TypeSymbol type, bool hasErrors = false) + public BoundCollectionExpression(SyntaxNode syntax, CollectionExpressionTypeKind collectionTypeKind, BoundObjectOrCollectionValuePlaceholder? placeholder, BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundCollectionBuilderElementsPlaceholder? collectionBuilderElementsPlaceholder, bool wasTargetTyped, bool hasWithElement, BoundUnconvertedCollectionExpression unconvertedCollectionExpression, ImmutableArray elements, TypeSymbol type, bool hasErrors = false) : base(BoundKind.CollectionExpression, syntax, elements, type, hasErrors || placeholder.HasErrors() || collectionCreation.HasErrors() || collectionBuilderElementsPlaceholder.HasErrors() || unconvertedCollectionExpression.HasErrors() || elements.HasErrors()) { @@ -6504,7 +6533,7 @@ public BoundCollectionExpression(SyntaxNode syntax, CollectionExpressionTypeKind public BoundObjectOrCollectionValuePlaceholder? Placeholder { get; } public BoundExpression? CollectionCreation { get; } public MethodSymbol? CollectionBuilderMethod { get; } - public BoundValuePlaceholder? CollectionBuilderElementsPlaceholder { get; } + public BoundCollectionBuilderElementsPlaceholder? CollectionBuilderElementsPlaceholder { get; } public bool WasTargetTyped { get; } public bool HasWithElement { get; } public BoundUnconvertedCollectionExpression UnconvertedCollectionExpression { get; } @@ -6512,7 +6541,7 @@ public BoundCollectionExpression(SyntaxNode syntax, CollectionExpressionTypeKind [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitCollectionExpression(this); - public BoundCollectionExpression Update(CollectionExpressionTypeKind collectionTypeKind, BoundObjectOrCollectionValuePlaceholder? placeholder, BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundValuePlaceholder? collectionBuilderElementsPlaceholder, bool wasTargetTyped, bool hasWithElement, BoundUnconvertedCollectionExpression unconvertedCollectionExpression, ImmutableArray elements, TypeSymbol type) + public BoundCollectionExpression Update(CollectionExpressionTypeKind collectionTypeKind, BoundObjectOrCollectionValuePlaceholder? placeholder, BoundExpression? collectionCreation, MethodSymbol? collectionBuilderMethod, BoundCollectionBuilderElementsPlaceholder? collectionBuilderElementsPlaceholder, bool wasTargetTyped, bool hasWithElement, BoundUnconvertedCollectionExpression unconvertedCollectionExpression, ImmutableArray elements, TypeSymbol type) { if (collectionTypeKind != this.CollectionTypeKind || placeholder != this.Placeholder || collectionCreation != this.CollectionCreation || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(collectionBuilderMethod, this.CollectionBuilderMethod) || collectionBuilderElementsPlaceholder != this.CollectionBuilderElementsPlaceholder || wasTargetTyped != this.WasTargetTyped || hasWithElement != this.HasWithElement || unconvertedCollectionExpression != this.UnconvertedCollectionExpression || elements != this.Elements || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { @@ -8950,6 +8979,8 @@ internal R VisitInternal(BoundNode node, A arg) return VisitSlicePatternReceiverPlaceholder((BoundSlicePatternReceiverPlaceholder)node, arg); case BoundKind.SlicePatternRangePlaceholder: return VisitSlicePatternRangePlaceholder((BoundSlicePatternRangePlaceholder)node, arg); + case BoundKind.CollectionBuilderElementsPlaceholder: + return VisitCollectionBuilderElementsPlaceholder((BoundCollectionBuilderElementsPlaceholder)node, arg); case BoundKind.Dup: return VisitDup((BoundDup)node, arg); case BoundKind.PassByCopy: @@ -9407,6 +9438,7 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitDup(BoundDup node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitPassByCopy(BoundPassByCopy node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitBadExpression(BoundBadExpression node, A arg) => this.DefaultVisit(node, arg); @@ -9644,6 +9676,7 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitDup(BoundDup node) => this.DefaultVisit(node); public virtual BoundNode? VisitPassByCopy(BoundPassByCopy node) => this.DefaultVisit(node); public virtual BoundNode? VisitBadExpression(BoundBadExpression node) => this.DefaultVisit(node); @@ -9901,6 +9934,7 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor public override BoundNode? VisitListPatternIndexPlaceholder(BoundListPatternIndexPlaceholder node) => null; public override BoundNode? VisitSlicePatternReceiverPlaceholder(BoundSlicePatternReceiverPlaceholder node) => null; public override BoundNode? VisitSlicePatternRangePlaceholder(BoundSlicePatternRangePlaceholder node) => null; + public override BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) => null; public override BoundNode? VisitDup(BoundDup node) => null; public override BoundNode? VisitPassByCopy(BoundPassByCopy node) { @@ -10988,6 +11022,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor TypeSymbol? type = this.VisitType(node.Type); return node.Update(type); } + public override BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(type); + } public override BoundNode? VisitDup(BoundDup node) { TypeSymbol? type = this.VisitType(node.Type); @@ -12001,7 +12040,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor MethodSymbol? collectionBuilderMethod = this.VisitMethodSymbol(node.CollectionBuilderMethod); BoundObjectOrCollectionValuePlaceholder? placeholder = node.Placeholder; BoundExpression? collectionCreation = (BoundExpression?)this.Visit(node.CollectionCreation); - BoundValuePlaceholder? collectionBuilderElementsPlaceholder = node.CollectionBuilderElementsPlaceholder; + BoundCollectionBuilderElementsPlaceholder? collectionBuilderElementsPlaceholder = node.CollectionBuilderElementsPlaceholder; BoundUnconvertedCollectionExpression unconvertedCollectionExpression = node.UnconvertedCollectionExpression; ImmutableArray elements = this.VisitList(node.Elements); TypeSymbol? type = this.VisitType(node.Type); @@ -12669,6 +12708,18 @@ public NullabilityRewriter(ImmutableDictionary elements = this.VisitList(node.Elements); BoundCollectionExpression updatedNode; @@ -15317,6 +15368,13 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); + public override TreeDumperNode VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node, object? arg) => new TreeDumperNode("collectionBuilderElementsPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); public override TreeDumperNode VisitDup(BoundDup node, object? arg) => new TreeDumperNode("dup", null, new TreeDumperNode[] { new TreeDumperNode("refKind", node.RefKind, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 7f21a9e68afd8..0ae60eefa98c2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -283,7 +283,7 @@ private PEModuleBuilder? EmitModule if (visited != null && visited != node && - node.Kind is not (BoundKind.ImplicitReceiver or BoundKind.ObjectOrCollectionValuePlaceholder or BoundKind.ValuePlaceholder)) + node.Kind is not (BoundKind.ImplicitReceiver or BoundKind.ObjectOrCollectionValuePlaceholder or BoundKind.ValuePlaceholder or BoundKind.CollectionBuilderElementsPlaceholder)) { if (!CanBePassedByReference(node) && CanBePassedByReference(visited)) { @@ -467,6 +467,11 @@ public override BoundNode VisitValuePlaceholder(BoundValuePlaceholder node) return PlaceholderReplacement(node); } + public override BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) + { + return PlaceholderReplacement(node); + } + public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) { return PlaceholderReplacement(node); @@ -1223,6 +1228,12 @@ public static void Validate(BoundNode node) return null; } + public override BoundNode? VisitCollectionBuilderElementsPlaceholder(BoundCollectionBuilderElementsPlaceholder node) + { + Fail(node); + return null; + } + public override BoundNode? VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) { Fail(node); diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 34fd39519fe59..0c7a463e841cf 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -322,18 +322,19 @@ public CSharpOperationFactory(SemanticModel semanticModel) _ => null }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); - case BoundKind.ValuePlaceholder: + case BoundKind.CollectionBuilderElementsPlaceholder: // The only supported use of BoundValuePlaceholder is within a collection expression as we use it to // represent the elements passed to the collection builder creation methods. We can hit these // creation methods when producing the .ConstructArguments for the ICollectionExpressionOperation. // Note: the caller will end up stripping this off when producing the ConstructArguments, so it will // not actually leak to the user. But this ends up keeping the logic simple between that callsite // and this code which actually hits all the arguments passed along. Because we never actually - // expose this placeholder in the IOp tree, it's fine to use .Unspecified as its kind here. + // expose this placeholder in the IOp tree, it's fine to use .CollectionBuilderElements as its kind + // here. // - // See the logic in CreateBoundCollectionExpression.getCreationArguments for more info. + // See the logic in CreateBoundCollectionExpression.getConstructArguments for more info. return new PlaceholderOperation( - PlaceholderKind.Unspecified, _semanticModel, boundNode.Syntax, + PlaceholderKind.CollectionBuilderElements, _semanticModel, boundNode.Syntax, boundNode switch { BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), @@ -1299,7 +1300,7 @@ static ImmutableArray getConstructArguments( if (boundCall.IsErroneousNode) { var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); - Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified }], + Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements }], "We should always have at least one argument (the placeholder elements)."); return array is [.. var normalArguments, _] ? normalArguments @@ -1311,7 +1312,7 @@ static ImmutableArray getConstructArguments( // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. // We do *not* want to include that information in the Arguments we return. - Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.Unspecified } }], + Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements } }], "We should always have at least one argument (the placeholder elements)."); var slicedArguments = arguments is [.. var normalArguments, _] ? normalArguments diff --git a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs index 42d5582bfd647..11614e564ebb9 100644 --- a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs +++ b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs @@ -11,5 +11,6 @@ internal enum PlaceholderKind ForToLoopBinaryOperatorLeftOperand = 2, ForToLoopBinaryOperatorRightOperand = 3, AggregationGroup = 4, + CollectionBuilderElements = 5, } } From b293d53beb8671dc23b75791a427681210f3424e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 18:48:20 +0100 Subject: [PATCH 63/92] Update docs --- .../Operations/CSharpOperationFactory.cs | 43 +++---------------- .../Generated/Operations.Generated.cs | 12 +++--- .../Operations/OperationInterfaces.xml | 12 +++--- .../Apis/Microsoft.CodeAnalysis.CSharp.txt | 15 ------- 4 files changed, 20 insertions(+), 62 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 0c7a463e841cf..51dd06b897190 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -323,16 +323,6 @@ public CSharpOperationFactory(SemanticModel semanticModel) }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.CollectionBuilderElementsPlaceholder: - // The only supported use of BoundValuePlaceholder is within a collection expression as we use it to - // represent the elements passed to the collection builder creation methods. We can hit these - // creation methods when producing the .ConstructArguments for the ICollectionExpressionOperation. - // Note: the caller will end up stripping this off when producing the ConstructArguments, so it will - // not actually leak to the user. But this ends up keeping the logic simple between that callsite - // and this code which actually hits all the arguments passed along. Because we never actually - // expose this placeholder in the IOp tree, it's fine to use .CollectionBuilderElements as its kind - // here. - // - // See the logic in CreateBoundCollectionExpression.getConstructArguments for more info. return new PlaceholderOperation( PlaceholderKind.CollectionBuilderElements, _semanticModel, boundNode.Syntax, boundNode switch @@ -1287,39 +1277,18 @@ static ImmutableArray getConstructArguments( // Match the logic in CreateBoundObjectCreationOperation which does not DeriveArguments in the case of an // problems encountered in binding. Debug.Assert(!objectCreation.Type.IsAnonymousType); - if (!CanDeriveObjectCreationExpressionArguments(objectCreation)) - return @this.CreateFromArray(((IBoundInvalidNode)objectCreation).InvalidNodeChildren); - - return ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); + return !CanDeriveObjectCreationExpressionArguments(objectCreation) + ? @this.CreateFromArray(((IBoundInvalidNode)objectCreation).InvalidNodeChildren) + : ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); } if (collectionCreation is BoundCall boundCall) { // Match the logic in CreateBoundCallOperation which does not DeriveArguments in the case of an // erroneous call node. - if (boundCall.IsErroneousNode) - { - var array = @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); - Debug.Assert(array is [.., IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements }], - "We should always have at least one argument (the placeholder elements)."); - return array is [.. var normalArguments, _] - ? normalArguments - : array; - } - else - { - var arguments = @this.DeriveArguments(collectionCreation); - - // With a CollectionBuilder, the last argument will be a placeholder where the .Elements will go. - // We do *not* want to include that information in the Arguments we return. - Debug.Assert(arguments is [.., IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements } }], - "We should always have at least one argument (the placeholder elements)."); - var slicedArguments = arguments is [.. var normalArguments, _] - ? normalArguments - : arguments; - - return ImmutableArray.CastUp(slicedArguments); - } + return boundCall.IsErroneousNode + ? @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren) + : ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); } if (collectionCreation is BoundNewT boundNewT) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 7c0896790a92f..fe98f435595dd 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3955,12 +3955,14 @@ public interface ICollectionExpressionOperation : IOperation /// IMethodSymbol? ConstructMethod { get; } /// - /// Arguments passed to a with(...) element on the collection expression, if any. - /// If the collection expression does not have a with(...) element, or does not allow - /// any arguments, this can be an empty array. Will never be default. If the with(...) element - /// successfully bound, these will all be ; otherwise, they can be - /// any operation. + /// Arguments passed to to , if present. Arguments are in evaluation order. This can + /// be an empty array. Will never be default. If the arguments succe successfully bound, these will all be + /// ; otherwise, they can be any operation. /// + /// + /// If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. + /// Default values are supplied for optional arguments missing in source. + /// ImmutableArray ConstructArguments { get; } /// /// Collection expression elements. diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index ad5905d4fc570..88e58728a2a2c 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3700,12 +3700,14 @@ - Arguments passed to a with(...) element on the collection expression, if any. - If the collection expression does not have a with(...) element, or does not allow - any arguments, this can be an empty array. Will never be default. If the with(...) element - successfully bound, these will all be ; otherwise, they can be - any operation. + Arguments passed to to , if present. Arguments are in evaluation order. This can + be an empty array. Will never be default. If the arguments succe successfully bound, these will all be + ; otherwise, they can be any operation. + + If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. + Default values are supplied for optional arguments missing in source. + diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt index 04bb64863b3af..3833122be11d6 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt @@ -568,7 +568,6 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWarningDirectiveTrivia(M Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) -Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -857,7 +856,6 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWarningDirectiveTrivia(Mi Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) -Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -1109,7 +1107,6 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWarningDirectiveTrivia( Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) -Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -4574,15 +4571,6 @@ Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_Condition Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_OpenParenToken Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_Statement Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_WhileKeyword -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.AddArgumentListArguments(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[]) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithWithKeyword(Microsoft.CodeAnalysis.SyntaxToken) -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.get_ArgumentList -Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.get_WithKeyword Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) @@ -5557,8 +5545,6 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhileStatement(Microsoft.CodeAnalysi Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhileStatement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Whitespace(System.String) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Whitespace(System.String,System.Boolean) -Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) -Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.XmlCDataSection(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken) @@ -6286,7 +6272,6 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhereKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileStatement Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhitespaceTrivia -Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithElement Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithExpression Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithInitializerExpression Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithKeyword From 0514752ab31a949e0b092578cf44fedfca5d70ad Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 19:04:20 +0100 Subject: [PATCH 64/92] CFG and tests --- .../Semantics/CollectionExpressionTests.cs | 25 ++++++++++ ...ectionExpressionTests_WithElement_Extra.cs | 49 +++++++++++-------- .../Operations/ControlFlowGraphBuilder.cs | 4 ++ .../Core/Compilation/OperationTreeVerifier.cs | 2 +- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index dc6b684d24ba0..96d8723f69de7 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16400,6 +16400,11 @@ .maxstack 1 VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -29116,6 +29121,11 @@ static MyCollection CreateCollection(MyCollection x, int y) Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, y]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: @@ -29178,6 +29188,11 @@ static IMyCollection CreateCollection(T a, T b) Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: IMyCollection) (Syntax: '[a, b]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') @@ -29208,6 +29223,11 @@ static ImmutableArray Create(ImmutableArray x, int y) VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: @@ -41066,6 +41086,11 @@ static void Main() model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[new()]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 93c7e0b506d3b..c8fc5dfbff43f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1544,31 +1544,40 @@ static void Main() var operation1 = semanticModel.GetOperation(arrowExpressions[0]); VerifyOperationTree(compilation, operation1, """ IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(), t]') - IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(), t]') - ReturnedValue: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(), t]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') - Elements(1): - IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(), t]') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(), t]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') """); var operation2 = semanticModel.GetOperation(arrowExpressions[1]); VerifyOperationTree(compilation, operation2, """ IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '=> [with(t), t]') - IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(t), t]') - ReturnedValue: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(t), t]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(T arg, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(t), t]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 't') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: '[with(t), t]') + ReturnedValue: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(t), t]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(T arg, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(t), t]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg) (OperationKind.Argument, Type: null) (Syntax: 't') + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(t)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Elements(1): - IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') """); } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 4ad329af20e15..d44e77f2a8d5a 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6556,6 +6556,10 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize } else { + arguments = arguments is [.. var nonPlaceholderArguments, IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements } }] + ? nonPlaceholderArguments + : arguments; + VisitAndPushArguments(arguments, instancePushed: false); } diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 8d789579b26cd..0680e4a7a2325 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1140,7 +1140,7 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) { LogString(nameof(IPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); - Assert.Equal(PlaceholderKind.AggregationGroup, operation.PlaceholderKind); + Assert.True(operation.PlaceholderKind is PlaceholderKind.AggregationGroup or PlaceholderKind.CollectionBuilderElements); } public override void VisitUnaryOperator(IUnaryOperation operation) From f33bfe6be93a21726651642d5393745c181183e3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 19:08:53 +0100 Subject: [PATCH 65/92] Update and add tests --- ...ionTests_ICollectionExpressionOperation.cs | 101 ++++++++++++++++-- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 9f1fd0e7c2909..94647007f9e5e 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -37,6 +37,31 @@ class MyHashSetBuilder } """; + private const string s_collectionBuilderOptionalConstructorArgType = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyHashSetBuilder), nameof(MyHashSetBuilder.Create))] + class MyHashSet : IEnumerable + { + public MyHashSet(int capacity = 42) { } + public void Add(int item) { } + + IEnumerator IEnumerable.GetEnumerator() => null!; + IEnumerator IEnumerable.GetEnumerator() => null; + } + + class MyHashSetBuilder + { + public static MyHashSet Create(ReadOnlySpan items) => null!; + public static MyHashSet Create(int capacity, ReadOnlySpan items) => null!; + public static MyHashSet Create(IEqualityComparer comparer, ReadOnlySpan items) => null!; + public static MyHashSet Create(int capacity, IEqualityComparer comparer, ReadOnlySpan items) => null!; + } + """; + [Fact] public void TestArray_Empty() { @@ -510,6 +535,31 @@ void M() """); } + [Fact] + public void TestObjectCreation_OptionalArg() + { + string source = $$""" + using System.Collections.Generic; + + class MyCollection : List + { + public MyCollection(int capacity = 42) : base(capacity) { } + } + + class C + { + void M() + { + MyCollection a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + + """); + } + [Fact] public void TestObjectCreation_SingleArg() { @@ -716,6 +766,11 @@ void M() var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -723,6 +778,24 @@ void M() """); } + [Fact] + public void TestCollectionBuilder_OptionalArg() + { + string source = """ + class C + { + void M() + { + MyHashSet a = [with(), 1, 2, 3]; + } + } + """; + var comp = CreateCompilation([source, s_collectionBuilderOptionalConstructorArgType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ + + """); + } + [Fact] public void TestCollectionBuilder_SingleArg() { @@ -737,12 +810,16 @@ void M() """; var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ - ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0), 1, 2, 3]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -846,7 +923,7 @@ void M() var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0, null), 1, 2, 3]') - ConstructArguments(2): + ConstructArguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -858,6 +935,10 @@ void M() ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -880,7 +961,7 @@ void M() var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') - ConstructArguments(2): + ConstructArguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -892,6 +973,10 @@ void M() ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -914,7 +999,7 @@ void M() var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(compa ... ), 1, 2, 3]') - ConstructArguments(2): + ConstructArguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) @@ -926,6 +1011,10 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') From 9d182f86365356705d9acc7f18a192e7ab1df691 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 19:12:26 +0100 Subject: [PATCH 66/92] Update and add tests --- ...ionTests_ICollectionExpressionOperation.cs | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 94647007f9e5e..87e8411a02c21 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -46,7 +46,6 @@ class MyHashSetBuilder [CollectionBuilder(typeof(MyHashSetBuilder), nameof(MyHashSetBuilder.Create))] class MyHashSet : IEnumerable { - public MyHashSet(int capacity = 42) { } public void Add(int item) { } IEnumerator IEnumerable.GetEnumerator() => null!; @@ -55,10 +54,7 @@ public void Add(int item) { } class MyHashSetBuilder { - public static MyHashSet Create(ReadOnlySpan items) => null!; - public static MyHashSet Create(int capacity, ReadOnlySpan items) => null!; - public static MyHashSet Create(IEqualityComparer comparer, ReadOnlySpan items) => null!; - public static MyHashSet Create(int capacity, IEqualityComparer comparer, ReadOnlySpan items) => null!; + public static MyHashSet Create(int capacity = 42, ReadOnlySpan items = default) => null!; } """; @@ -550,13 +546,22 @@ class C { void M() { - MyCollection a = [with(), 1, 2, 3]; + MyCollection a = [with(), 1, 2, 3]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ - + ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection..ctor([System.Int32 capacity = 42])) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: capacity) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } @@ -792,7 +797,20 @@ void M() """; var comp = CreateCompilation([source, s_collectionBuilderOptionalConstructorArgType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ - + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create([System.Int32 capacity = 42], [System.ReadOnlySpan items = default(System.ReadOnlySpan)])) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: capacity) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); } @@ -810,14 +828,14 @@ void M() """; var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ - ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0), 1, 2, 3]') ConstructArguments(2): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -842,11 +860,15 @@ void M() var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') - ConstructArguments(1): + ConstructArguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') From 5daf3a8006ebc0269f5381802a39c87dee96db24 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 19:13:15 +0100 Subject: [PATCH 67/92] Tweak docs --- src/Compilers/Core/Portable/Generated/Operations.Generated.cs | 4 ++-- .../Core/Portable/Operations/OperationInterfaces.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index fe98f435595dd..fea63c45d17aa 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3955,8 +3955,8 @@ public interface ICollectionExpressionOperation : IOperation /// IMethodSymbol? ConstructMethod { get; } /// - /// Arguments passed to to , if present. Arguments are in evaluation order. This can - /// be an empty array. Will never be default. If the arguments succe successfully bound, these will all be + /// Arguments passed to to , if present. Arguments are in evaluation order. This + /// can be an empty array. Will never be default. If the arguments successfully bound, these will all be /// ; otherwise, they can be any operation. /// /// diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 88e58728a2a2c..8c9f0e3c86901 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3701,7 +3701,7 @@ Arguments passed to to , if present. Arguments are in evaluation order. This can - be an empty array. Will never be default. If the arguments succe successfully bound, these will all be + be an empty array. Will never be default. If the arguments successfully bound, these will all be ; otherwise, they can be any operation. From 9769388e5f12c744a6d14a10c215ce9cc3f3f47d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 19:13:36 +0100 Subject: [PATCH 68/92] Tweak docs --- src/Compilers/Core/Portable/Generated/Operations.Generated.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index fea63c45d17aa..e08431402ea1c 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3955,8 +3955,8 @@ public interface ICollectionExpressionOperation : IOperation /// IMethodSymbol? ConstructMethod { get; } /// - /// Arguments passed to to , if present. Arguments are in evaluation order. This - /// can be an empty array. Will never be default. If the arguments successfully bound, these will all be + /// Arguments passed to to , if present. Arguments are in evaluation order. This can + /// be an empty array. Will never be default. If the arguments successfully bound, these will all be /// ; otherwise, they can be any operation. /// /// From e07de895f89e196912acd80b802dd9e77032caf1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 19 Nov 2025 21:00:45 +0100 Subject: [PATCH 69/92] Update --- .../Apis/Microsoft.CodeAnalysis.CSharp.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt index 3833122be11d6..04bb64863b3af 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt @@ -568,6 +568,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWarningDirectiveTrivia(M Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -856,6 +857,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWarningDirectiveTrivia(Mi Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -1107,6 +1109,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWarningDirectiveTrivia( Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhenClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhereClause(Microsoft.CodeAnalysis.CSharp.Syntax.WhereClauseSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWhileStatement(Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWithElement(Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitWithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitXmlCDataSection(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCDataSectionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitXmlComment(Microsoft.CodeAnalysis.CSharp.Syntax.XmlCommentSyntax) @@ -4571,6 +4574,15 @@ Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_Condition Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_OpenParenToken Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_Statement Microsoft.CodeAnalysis.CSharp.Syntax.WhileStatementSyntax.get_WhileKeyword +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.AddArgumentListArguments(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.WithWithKeyword(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.get_ArgumentList +Microsoft.CodeAnalysis.CSharp.Syntax.WithElementSyntax.get_WithKeyword Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) @@ -5545,6 +5557,8 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhileStatement(Microsoft.CodeAnalysi Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WhileStatement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Whitespace(System.String) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Whitespace(System.String,System.Boolean) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithElement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.XmlCDataSection(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken) @@ -6272,6 +6286,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhereKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileStatement Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhitespaceTrivia +Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithElement Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithExpression Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithInitializerExpression Microsoft.CodeAnalysis.CSharp.SyntaxKind.WithKeyword From 9f12cd55e5c17fb7f4c10813f14b6a9540fdae7d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:01:08 +0100 Subject: [PATCH 70/92] More CFG work --- .../Test/Emit3/Semantics/CollectionExpressionTests.cs | 5 +++++ .../Core/Portable/Operations/ControlFlowGraphBuilder.cs | 7 +++---- .../Test/Core/Compilation/ControlFlowGraphVerifier.cs | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 96d8723f69de7..290b5a5e60c05 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -29256,6 +29256,11 @@ static ImmutableArray Create(ImmutableArray x, int y) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index d44e77f2a8d5a..1a7e10a336d72 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6556,10 +6556,6 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize } else { - arguments = arguments is [.. var nonPlaceholderArguments, IArgumentOperation { Value: IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements } }] - ? nonPlaceholderArguments - : arguments; - VisitAndPushArguments(arguments, instancePushed: false); } @@ -7439,6 +7435,9 @@ internal override IOperation VisitPlaceholder(IPlaceholderOperation operation, i return OperationCloner.CloneOperation(_currentAggregationGroup); } break; + case PlaceholderKind.CollectionBuilderElements: + // Leave collection builder element placeholder alone. It itself doesn't affect flow control. + return new PlaceholderOperation(operation.PlaceholderKind, semanticModel: null, operation.Syntax, operation.Type, operation.IsImplicit); } Debug.Fail("All placeholders should be handled above. Have we introduced a new scenario where placeholders are used?"); diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index 8384b3c4572bc..8f70a8828934b 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -1897,6 +1897,9 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && simpleAssignment.Parent.Kind == OperationKind.AnonymousObjectCreation); case OperationKind.None: + if (n is IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements }) + return true; + return !(n is IPlaceholderOperation); case OperationKind.FunctionPointerInvocation: From 7d8fec28c5727883ac6db037f60cc3764738582b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:19:01 +0100 Subject: [PATCH 71/92] Add new iop type --- ...ionTests_ICollectionExpressionOperation.cs | 607 ++++++++++++++++++ .../Generated/OperationKind.Generated.cs | 4 +- .../Generated/Operations.Generated.cs | 42 ++ .../Operations/OperationInterfaces.xml | 11 + 4 files changed, 663 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 87e8411a02c21..6e41f366e503d 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -84,6 +85,40 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Int32[] a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -114,6 +149,42 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Int32[] a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -144,6 +215,42 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Int32[] a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32[], IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[], IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -173,6 +280,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -204,6 +317,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -235,6 +354,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Theory] @@ -264,6 +389,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Theory] @@ -298,6 +429,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Theory] @@ -332,6 +469,12 @@ void M() var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var operation = semanticModel.GetOperation(comp.SyntaxTrees.Single().FindNodeOrTokenByKind(SyntaxKind.WithElement).AsNode()!); Assert.Null(operation); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Theory] @@ -357,6 +500,40 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.ICollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Theory] @@ -387,6 +564,45 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.ICollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Theory] @@ -417,6 +633,45 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.ICollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Theory] @@ -447,6 +702,42 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.ICollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: '[with(unkno ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Theory] @@ -478,6 +769,43 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.ICollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: '[with(0, 1), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection, IsInvalid) (Syntax: '[with(0, 1), 1, 2, 3]') + ConstructArguments(2): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -501,6 +829,40 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -529,6 +891,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -563,6 +931,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -591,6 +965,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -626,6 +1006,53 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(0, null), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(0, null), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -661,6 +1088,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -696,6 +1129,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -726,6 +1165,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -754,6 +1199,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -781,6 +1232,45 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -812,6 +1302,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -843,6 +1339,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -874,6 +1376,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -901,6 +1409,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -928,6 +1442,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -966,6 +1486,57 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(0, null), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0, null), 1, 2, 3]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') + IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] @@ -1004,6 +1575,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -1042,6 +1619,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -1071,6 +1654,12 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -1094,6 +1683,12 @@ class C ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -1122,6 +1717,12 @@ class C ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } [Fact] @@ -1150,5 +1751,11 @@ class C ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); + VerifyFlowGraph(comp, method, """ + + """); } } diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index be6e5990ace6a..e81a0971df6a2 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -283,7 +283,9 @@ public enum OperationKind InlineArrayAccess = 0x7e, /// Indicates an . CollectionExpression = 0x7f, + /// Indicates an . + CollectionExpressionElements = 0x80, /// Indicates an . - Spread = 0x80, + Spread = 0x81, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index e08431402ea1c..895553355d7f3 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3975,6 +3975,24 @@ public interface ICollectionExpressionOperation : IOperation ImmutableArray Elements { get; } } /// + /// Represents the elements of a collection expression as they are passed to some construction method + /// specified by a [CollectionBuilder] attribute. This is distinct from + /// which contains the elements as they appear in source. This will appear in + /// when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that + /// construction method containing the fully evaluated elements of the collection expression. + /// + /// + /// This node is associated with the following operation kinds: + /// + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface ICollectionExpressionElementsOperation : IOperation + { + } + /// /// Represents a collection expression spread element. /// /// Current usage: @@ -10753,6 +10771,23 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpression(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpression(this, argument); } + internal sealed partial class CollectionExpressionElementsOperation : Operation, ICollectionExpressionElementsOperation + { + internal CollectionExpressionElementsOperation(SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + : base(semanticModel, syntax, isImplicit) + { + Type = type; + } + internal override int ChildOperationsCount => 0; + internal override IOperation GetCurrent(int slot, int index) => throw ExceptionUtilities.UnexpectedValue((slot, index)); + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); + public override ITypeSymbol? Type { get; } + internal override ConstantValue? OperationConstantValue => null; + public override OperationKind Kind => OperationKind.CollectionExpressionElements; + public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpressionElements(this); + public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpressionElements(this, argument); + } internal sealed partial class SpreadOperation : Operation, ISpreadOperation { internal SpreadOperation(IOperation operand, ITypeSymbol? elementType, IConvertibleConversion elementConversion, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit) @@ -11427,6 +11462,11 @@ public override IOperation VisitCollectionExpression(ICollectionExpressionOperat var internalOperation = (CollectionExpressionOperation)operation; return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.ConstructArguments), VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } + public override IOperation VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, object? argument) + { + var internalOperation = (CollectionExpressionElementsOperation)operation; + return new CollectionExpressionElementsOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + } public override IOperation VisitSpread(ISpreadOperation operation, object? argument) { var internalOperation = (SpreadOperation)operation; @@ -11573,6 +11613,7 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitAttribute(IAttributeOperation operation) => DefaultVisit(operation); public virtual void VisitInlineArrayAccess(IInlineArrayAccessOperation operation) => DefaultVisit(operation); public virtual void VisitCollectionExpression(ICollectionExpressionOperation operation) => DefaultVisit(operation); + public virtual void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) => DefaultVisit(operation); public virtual void VisitSpread(ISpreadOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor @@ -11712,6 +11753,7 @@ public abstract partial class OperationVisitor public virtual TResult? VisitAttribute(IAttributeOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitInlineArrayAccess(IInlineArrayAccessOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitCollectionExpression(ICollectionExpressionOperation operation, TArgument argument) => DefaultVisit(operation, argument); + public virtual TResult? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitSpread(ISpreadOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 8c9f0e3c86901..e4a411a079c48 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3723,6 +3723,17 @@ + + + + Represents the elements of a collection expression as they are passed to some construction method + specified by a [CollectionBuilder] attribute. This is distinct from + which contains the elements as they appear in source. This will appear in + when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that + construction method containing the fully evaluated elements of the collection expression. + + + From 8847d30edb8d5d96dcbfd4bc57a67182dd7356c2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:29:14 +0100 Subject: [PATCH 72/92] Thread through and update tests --- .../Operations/CSharpOperationFactory.cs | 4 +- .../Semantics/CollectionExpressionTests.cs | 38 +++++++++---------- ...ectionExpressionTests_WithElement_Extra.cs | 2 +- .../Operations/ControlFlowGraphBuilder.cs | 9 +++-- .../Portable/Operations/PlaceholderKind.cs | 1 - .../Compilation/ControlFlowGraphVerifier.cs | 4 +- .../Core/Compilation/OperationTreeVerifier.cs | 8 +++- .../Core/Compilation/TestOperationVisitor.cs | 6 +++ 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 51dd06b897190..0806523f37fff 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -323,8 +323,8 @@ public CSharpOperationFactory(SemanticModel semanticModel) }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.CollectionBuilderElementsPlaceholder: - return new PlaceholderOperation( - PlaceholderKind.CollectionBuilderElements, _semanticModel, boundNode.Syntax, + return new CollectionExpressionElementsOperation( + _semanticModel, boundNode.Syntax, boundNode switch { BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 290b5a5e60c05..9630487de0703 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16397,14 +16397,13 @@ .maxstack 1 """); // We should extend IOperation conversions to represent IsCollectionExpression // Tracked by https://github.com/dotnet/roslyn/issues/68826 - VerifyOperationTreeForTest(comp, - """ + VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[1, 2, 3]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -29113,8 +29112,7 @@ static MyCollection CreateCollection(MyCollection x, int y) var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); - var operation = VerifyOperationTreeForTest(comp, - """ + var operation = VerifyOperationTreeForTest(comp, """ IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return [..x, y];') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[..x, y]') @@ -29123,7 +29121,7 @@ static MyCollection CreateCollection(MyCollection x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29180,19 +29178,18 @@ static IMyCollection CreateCollection(T a, T b) var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); - var operation = VerifyOperationTreeForTest(comp, - """ + var operation = VerifyOperationTreeForTest(comp, """ IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return [a, b];') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: IMyCollection, IsImplicit) (Syntax: '[a, b]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: IMyCollection) (Syntax: '[a, b]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') @@ -29220,12 +29217,11 @@ static ImmutableArray Create(ImmutableArray x, int y) var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); - VerifyOperationTreeForTest(comp, - """ + VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -41093,7 +41089,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[new()]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index c8fc5dfbff43f..45c8a71aa5578 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1552,7 +1552,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 1a7e10a336d72..2f8942c1ea5ee 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -7435,15 +7435,18 @@ internal override IOperation VisitPlaceholder(IPlaceholderOperation operation, i return OperationCloner.CloneOperation(_currentAggregationGroup); } break; - case PlaceholderKind.CollectionBuilderElements: - // Leave collection builder element placeholder alone. It itself doesn't affect flow control. - return new PlaceholderOperation(operation.PlaceholderKind, semanticModel: null, operation.Syntax, operation.Type, operation.IsImplicit); } Debug.Fail("All placeholders should be handled above. Have we introduced a new scenario where placeholders are used?"); return new PlaceholderOperation(operation.PlaceholderKind, semanticModel: null, operation.Syntax, operation.Type, IsImplicit(operation)); } + public override IOperation? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, int? argument) + { + // Leave collection builder element placeholder alone. It itself doesn't affect flow control. + return new CollectionExpressionElementsOperation(semanticModel: null, operation.Syntax, operation.Type, operation.IsImplicit); + } + public override IOperation VisitConversion(IConversionOperation operation, int? captureIdForResult) { return new ConversionOperation(VisitRequired(operation.Operand), ((ConversionOperation)operation).ConversionConvertible, operation.IsTryCast, operation.IsChecked, semanticModel: null, operation.Syntax, operation.Type, operation.GetConstantValue(), IsImplicit(operation)); diff --git a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs index 11614e564ebb9..42d5582bfd647 100644 --- a/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs +++ b/src/Compilers/Core/Portable/Operations/PlaceholderKind.cs @@ -11,6 +11,5 @@ internal enum PlaceholderKind ForToLoopBinaryOperatorLeftOperand = 2, ForToLoopBinaryOperatorRightOperand = 3, AggregationGroup = 4, - CollectionBuilderElements = 5, } } diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index 8f70a8828934b..c397346209ef6 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -1897,9 +1897,6 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && simpleAssignment.Parent.Kind == OperationKind.AnonymousObjectCreation); case OperationKind.None: - if (n is IPlaceholderOperation { PlaceholderKind: PlaceholderKind.CollectionBuilderElements }) - return true; - return !(n is IPlaceholderOperation); case OperationKind.FunctionPointerInvocation: @@ -1982,6 +1979,7 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.InlineArrayAccess: case OperationKind.CollectionExpression: case OperationKind.Spread: + case OperationKind.CollectionExpressionElements: return true; } diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 0680e4a7a2325..60106a2da0794 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1140,7 +1140,13 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) { LogString(nameof(IPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); - Assert.True(operation.PlaceholderKind is PlaceholderKind.AggregationGroup or PlaceholderKind.CollectionBuilderElements); + Assert.True(operation.PlaceholderKind is PlaceholderKind.AggregationGroup); + } + + public override void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) + { + LogString(nameof(ICollectionExpressionElementsOperation)); + LogCommonPropertiesAndNewLine(operation); } public override void VisitUnaryOperator(IUnaryOperation operation) diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index 6a8049100dec7..16b5f1cfc5049 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -743,6 +743,12 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) Assert.Empty(operation.ChildOperations); } + public override void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) + { + Assert.Equal(OperationKind.CollectionExpressionElements, operation.Kind); + Assert.Empty(operation.ChildOperations); + } + public override void VisitUnaryOperator(IUnaryOperation operation) { Assert.Equal(OperationKind.UnaryOperator, operation.Kind); From 8393c637e338530badd76169423a812b4055edd9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:30:09 +0100 Subject: [PATCH 73/92] Thread through and update tests --- .../CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs | 5 ++--- .../Semantics/CollectionExpressionTests_WithElement_Extra.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 9630487de0703..44244e363c7ab 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -29238,8 +29238,7 @@ static ImmutableArray Create(ImmutableArray x, int y) var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); - VerifyFlowGraph(comp, method, - """ + VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] @@ -29254,7 +29253,7 @@ static ImmutableArray Create(ImmutableArray x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 45c8a71aa5578..a604d8ddd6587 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1573,7 +1573,7 @@ static void Main() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(t)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): From 033fa455ee301e3bec3fb73e7cce96017ff44d0e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:37:06 +0100 Subject: [PATCH 74/92] Update tests --- ...ionTests_ICollectionExpressionOperation.cs | 574 +++++++++++++++++- 1 file changed, 548 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 6e41f366e503d..0fad9abf37f89 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -284,7 +284,35 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Span a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Span, IsInvalid, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -321,7 +349,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Span a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Span, IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -358,7 +416,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Span a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Span, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Span, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -393,7 +481,35 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.IEnumerable a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -433,7 +549,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.IEnumerable a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -473,7 +619,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.IEnumerable a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -503,7 +679,7 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] @@ -514,11 +690,11 @@ void M() Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: '[with(), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: @@ -895,7 +1071,40 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -935,7 +1144,40 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection..ctor([System.Int32 capacity = 42])) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: capacity) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -969,7 +1211,40 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1092,7 +1367,48 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1133,7 +1449,48 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsImplicit) (Syntax: '[with(compa ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.HashSet..ctor(System.Int32 capacity, System.Collections.Generic.IEqualityComparer? comparer)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet) (Syntax: '[with(compa ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1169,7 +1526,39 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + ConstructArguments(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1203,7 +1592,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.HashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(""), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(""), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.HashSet, IsInvalid, IsImplicit) (Syntax: '[with(""), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.HashSet, IsInvalid) (Syntax: '[with(""), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1231,6 +1650,17 @@ void M() ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Actual: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); var tree = comp.SyntaxTrees[0]; @@ -1294,7 +1724,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1331,7 +1761,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1368,7 +1798,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1413,7 +1843,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: '[with(unkno ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1446,7 +1906,37 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1478,7 +1968,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1567,7 +2057,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1611,7 +2101,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1658,7 +2148,39 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsInvalid, IsImplicit) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyHashSet, IsInvalid) (Syntax: '[with(0, nu ... ), 1, 2, 3]') + ConstructArguments(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "", IsInvalid) (Syntax: '""') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } From 393762fc426e39fb15c34b7ceabb854b00025139 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:42:26 +0100 Subject: [PATCH 75/92] Update tests --- ...ionTests_ICollectionExpressionOperation.cs | 404 +++++++++++++++--- 1 file changed, 341 insertions(+), 63 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 0fad9abf37f89..ec85b742e2741 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -480,26 +480,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.IEnumerable a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: '[with(), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[with(), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(), 1, 2, 3]') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -548,26 +548,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.IEnumerable a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') ConstructArguments(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): @@ -618,26 +618,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.IEnumerable a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEnumerable, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') ConstructArguments(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): @@ -686,7 +686,7 @@ void M() Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.ICollection a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) @@ -743,26 +743,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.ICollection a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(0), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(0), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -812,26 +812,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.ICollection a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(capac ... ), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(capac ... ), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') @@ -881,26 +881,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.ICollection a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: '[with(unkno ... ), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: '[with(unkno ... ), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(unkno ... ), 1, 2, 3]') ConstructArguments(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Elements(3): @@ -948,26 +948,26 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); - VerifyFlowGraph(comp, method, """ + VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { - Locals: [System.Collections.Generic.ICollection a] + Locals: [System.Collections.Generic.{{typeName}} a] Block[B1] - Block Predecessors: [B0] Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') Left: - ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.ICollection, IsInvalid, IsImplicit) (Syntax: '[with(0, 1), 1, 2, 3]') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{typeName}}, IsInvalid, IsImplicit) (Syntax: '[with(0, 1), 1, 2, 3]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection, IsInvalid) (Syntax: '[with(0, 1), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}, IsInvalid) (Syntax: '[with(0, 1), 1, 2, 3]') ConstructArguments(2): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') @@ -1640,17 +1640,6 @@ void M() """; var comp = CreateCompilation([source, s_collectionBuilderType], targetFramework: TargetFramework.Net90).VerifyDiagnostics(); comp.VerifyOperationTree(comp.SyntaxTrees.First().FindNodeOrTokenByKind(SyntaxKind.CollectionExpression).AsNode(), """ - ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Elements(3): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') - Actual: ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') @@ -1736,7 +1725,44 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create([System.Int32 capacity = 42], [System.ReadOnlySpan items = default(System.ReadOnlySpan)])) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: capacity) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1773,7 +1799,44 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -1810,7 +1873,44 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -2013,7 +2113,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2069,7 +2169,52 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -2113,7 +2258,52 @@ void M() var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyHashSet a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: MyHashSet, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyHashSet, IsImplicit) (Syntax: '[with(compa ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.Int32 capacity, System.Collections.Generic.IEqualityComparer comparer, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(compa ... ), 1, 2, 3]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: comparer) (OperationKind.Argument, Type: null) (Syntax: 'comparer: null') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IEqualityComparer, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'capacity: 0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -2209,7 +2399,35 @@ class C var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [T a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: T, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: T, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: '[with(), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T) (Syntax: '[with(), 1, 2, 3]') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -2243,7 +2461,37 @@ class C var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [T a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: T, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: T, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsInvalid, IsImplicit) (Syntax: '[with(0), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T, IsInvalid) (Syntax: '[with(0), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } @@ -2277,7 +2525,37 @@ class C var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "M"); VerifyFlowGraph(comp, method, """ - + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [T a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: T, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Left: + ILocalReferenceOperation: a (IsDeclaration: True) (OperationKind.LocalReference, Type: T, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsInvalid, IsImplicit) (Syntax: '[with(capac ... ), 1, 2, 3]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: T, IsInvalid) (Syntax: '[with(capac ... ), 1, 2, 3]') + ConstructArguments(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') + Elements(3): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) """); } } From dfe52e6a2f9effe69c24f504adf50bf20d11bde1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:43:34 +0100 Subject: [PATCH 76/92] Update tests --- .../IOperationTests_ICollectionExpressionOperation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index ec85b742e2741..189f26cee363a 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -698,7 +698,7 @@ void M() Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: - ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.ICollection) (Syntax: '[with(), 1, 2, 3]') + ICollectionExpressionOperation (3 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{typeName}}) (Syntax: '[with(), 1, 2, 3]') Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -1676,7 +1676,7 @@ void M() ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - IPlaceholderOperation (OperationKind.None, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): From db3fba05c87d72bdb27879bd46b0c0584c15f136 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 11:57:13 +0100 Subject: [PATCH 77/92] Update tests --- ...ectionExpressionTests_WithElement_Extra.cs | 125 ++++++++++++++++++ .../Operations/ControlFlowGraphBuilder.cs | 6 +- 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index a604d8ddd6587..79f75ee3e5b88 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -8035,4 +8035,129 @@ static void Main(string[] args) Statements (0) """, graph, symbol); } + + [Fact] + public void ControlFlowBuilder() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int capacity, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') + Value: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(Compu ... seBranch()]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(ComputeCapacity())') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + Next (Regular) Block[B5] + Leaving: {R1} + } + Block[B5] - Exit + Predecessors: [B4] + Statements (0) + """, graph, symbol); + } } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 2f8942c1ea5ee..fd4cdc3309c3d 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -1280,7 +1280,8 @@ private void AddStatement( || slot.operationOpt.Kind == OperationKind.FlowCaptureReference || slot.operationOpt.Kind == OperationKind.DeclarationExpression || slot.operationOpt.Kind == OperationKind.Discard - || slot.operationOpt.Kind == OperationKind.OmittedArgument)); + || slot.operationOpt.Kind == OperationKind.OmittedArgument + || slot.operationOpt.Kind == OperationKind.CollectionExpressionElements)); #endif if (statement == null) { @@ -1849,7 +1850,8 @@ private void SpillEvalStack() if (operationOpt.Kind != OperationKind.FlowCaptureReference && operationOpt.Kind != OperationKind.DeclarationExpression && operationOpt.Kind != OperationKind.Discard - && operationOpt.Kind != OperationKind.OmittedArgument) + && operationOpt.Kind != OperationKind.OmittedArgument + && operationOpt.Kind != OperationKind.CollectionExpressionElements) { // Here we need to decide what region should own the new capture. Due to the spilling operations occurred before, // we currently might be in a region that is not associated with the stack frame we are in, but it is one of its From 70a5174d4d4c2c762dab7301ad49c9273981b357 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 20:15:55 +0100 Subject: [PATCH 78/92] Move to end --- .../Generated/OperationKind.Generated.cs | 6 +- .../Generated/Operations.Generated.cs | 84 +++++++++---------- .../Operations/OperationInterfaces.xml | 22 ++--- 3 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index e81a0971df6a2..00fb0aaa3228b 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -283,9 +283,9 @@ public enum OperationKind InlineArrayAccess = 0x7e, /// Indicates an . CollectionExpression = 0x7f, - /// Indicates an . - CollectionExpressionElements = 0x80, /// Indicates an . - Spread = 0x81, + Spread = 0x80, + /// Indicates an . + CollectionExpressionElements = 0x81, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 895553355d7f3..92a2ae4303db4 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3975,24 +3975,6 @@ public interface ICollectionExpressionOperation : IOperation ImmutableArray Elements { get; } } /// - /// Represents the elements of a collection expression as they are passed to some construction method - /// specified by a [CollectionBuilder] attribute. This is distinct from - /// which contains the elements as they appear in source. This will appear in - /// when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that - /// construction method containing the fully evaluated elements of the collection expression. - /// - /// - /// This node is associated with the following operation kinds: - /// - /// - /// - /// This interface is reserved for implementation by its associated APIs. We reserve the right to - /// change it in the future. - /// - public interface ICollectionExpressionElementsOperation : IOperation - { - } - /// /// Represents a collection expression spread element. /// /// Current usage: @@ -4025,6 +4007,24 @@ public interface ISpreadOperation : IOperation /// CommonConversion ElementConversion { get; } } + /// + /// Represents the elements of a collection expression as they are passed to some construction method + /// specified by a [CollectionBuilder] attribute. This is distinct from + /// which contains the elements as they appear in source. This will appear in + /// when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that + /// construction method containing the fully evaluated elements of the collection expression. + /// + /// + /// This node is associated with the following operation kinds: + /// + /// + /// + /// This interface is reserved for implementation by its associated APIs. We reserve the right to + /// change it in the future. + /// + public interface ICollectionExpressionElementsOperation : IOperation + { + } #endregion #region Implementations @@ -10771,23 +10771,6 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpression(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpression(this, argument); } - internal sealed partial class CollectionExpressionElementsOperation : Operation, ICollectionExpressionElementsOperation - { - internal CollectionExpressionElementsOperation(SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) - : base(semanticModel, syntax, isImplicit) - { - Type = type; - } - internal override int ChildOperationsCount => 0; - internal override IOperation GetCurrent(int slot, int index) => throw ExceptionUtilities.UnexpectedValue((slot, index)); - internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); - internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); - public override ITypeSymbol? Type { get; } - internal override ConstantValue? OperationConstantValue => null; - public override OperationKind Kind => OperationKind.CollectionExpressionElements; - public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpressionElements(this); - public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpressionElements(this, argument); - } internal sealed partial class SpreadOperation : Operation, ISpreadOperation { internal SpreadOperation(IOperation operand, ITypeSymbol? elementType, IConvertibleConversion elementConversion, SemanticModel? semanticModel, SyntaxNode syntax, bool isImplicit) @@ -10844,6 +10827,23 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i public override void Accept(OperationVisitor visitor) => visitor.VisitSpread(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitSpread(this, argument); } + internal sealed partial class CollectionExpressionElementsOperation : Operation, ICollectionExpressionElementsOperation + { + internal CollectionExpressionElementsOperation(SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + : base(semanticModel, syntax, isImplicit) + { + Type = type; + } + internal override int ChildOperationsCount => 0; + internal override IOperation GetCurrent(int slot, int index) => throw ExceptionUtilities.UnexpectedValue((slot, index)); + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); + internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); + public override ITypeSymbol? Type { get; } + internal override ConstantValue? OperationConstantValue => null; + public override OperationKind Kind => OperationKind.CollectionExpressionElements; + public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpressionElements(this); + public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpressionElements(this, argument); + } #endregion #region Cloner internal sealed partial class OperationCloner : OperationVisitor @@ -11462,16 +11462,16 @@ public override IOperation VisitCollectionExpression(ICollectionExpressionOperat var internalOperation = (CollectionExpressionOperation)operation; return new CollectionExpressionOperation(internalOperation.ConstructMethod, VisitArray(internalOperation.ConstructArguments), VisitArray(internalOperation.Elements), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } - public override IOperation VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, object? argument) - { - var internalOperation = (CollectionExpressionElementsOperation)operation; - return new CollectionExpressionElementsOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); - } public override IOperation VisitSpread(ISpreadOperation operation, object? argument) { var internalOperation = (SpreadOperation)operation; return new SpreadOperation(Visit(internalOperation.Operand), internalOperation.ElementType, internalOperation.ElementConversionConvertible, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit); } + public override IOperation VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, object? argument) + { + var internalOperation = (CollectionExpressionElementsOperation)operation; + return new CollectionExpressionElementsOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + } } #endregion @@ -11613,8 +11613,8 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitAttribute(IAttributeOperation operation) => DefaultVisit(operation); public virtual void VisitInlineArrayAccess(IInlineArrayAccessOperation operation) => DefaultVisit(operation); public virtual void VisitCollectionExpression(ICollectionExpressionOperation operation) => DefaultVisit(operation); - public virtual void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) => DefaultVisit(operation); public virtual void VisitSpread(ISpreadOperation operation) => DefaultVisit(operation); + public virtual void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11753,8 +11753,8 @@ public abstract partial class OperationVisitor public virtual TResult? VisitAttribute(IAttributeOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitInlineArrayAccess(IInlineArrayAccessOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitCollectionExpression(ICollectionExpressionOperation operation, TArgument argument) => DefaultVisit(operation, argument); - public virtual TResult? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitSpread(ISpreadOperation operation, TArgument argument) => DefaultVisit(operation, argument); + public virtual TResult? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion } diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index e4a411a079c48..132d3ce8624e4 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3723,17 +3723,6 @@ - - - - Represents the elements of a collection expression as they are passed to some construction method - specified by a [CollectionBuilder] attribute. This is distinct from - which contains the elements as they appear in source. This will appear in - when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that - construction method containing the fully evaluated elements of the collection expression. - - - @@ -3767,4 +3756,15 @@ + + + + Represents the elements of a collection expression as they are passed to some construction method + specified by a [CollectionBuilder] attribute. This is distinct from + which contains the elements as they appear in source. This will appear in + when the construction method is a collection builder method, representing the final ReadOnlySpan passed to that + construction method containing the fully evaluated elements of the collection expression. + + + From bddf4331eb00269e840f37e0c925ebb91a93931e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 20:17:04 +0100 Subject: [PATCH 79/92] Update public api --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 225c95dd5919c..d8e4839133a8f 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,6 +21,8 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.ReduceExtensionMember(Microsoft.CodeAnalysis.ITypeSymbol! receiverType) -> Microsoft.CodeAnalysis.IPropertySymbol? +Microsoft.CodeAnalysis.OperationKind.CollectionExpressionElements = 129 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.ConstructArguments.get -> System.Collections.Immutable.ImmutableArray static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool @@ -51,3 +53,5 @@ const Microsoft.CodeAnalysis.WellKnownMemberNames.CheckedDecrementAssignmentOper const Microsoft.CodeAnalysis.WellKnownMemberNames.CheckedIncrementAssignmentOperatorName = "op_CheckedIncrementAssignment" -> string! const Microsoft.CodeAnalysis.WellKnownMemberNames.DecrementAssignmentOperatorName = "op_DecrementAssignment" -> string! const Microsoft.CodeAnalysis.WellKnownMemberNames.IncrementAssignmentOperatorName = "op_IncrementAssignment" -> string! +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElements(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation! operation) -> void +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElements(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation! operation, TArgument argument) -> TResult? From c59c928db7e749b3d8e1057795f1ed9892c77868 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 20:25:06 +0100 Subject: [PATCH 80/92] Move tests --- ...ectionExpressionTests_WithElement_Extra.cs | 227 ------------------ ...ionTests_ICollectionExpressionOperation.cs | 227 ++++++++++++++++++ 2 files changed, 227 insertions(+), 227 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 79f75ee3e5b88..42b7f9e5c824f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -7933,231 +7933,4 @@ void N(params List list) { } // N(with(capacity: 0), 1, 2, 3); Diagnostic(ErrorCode.ERR_NameNotInContext, "with").WithArguments("with").WithLocation(6, 11)); } - - [Fact] - public void ControlFlow() - { - var source = """ - using System.Collections.Generic; - class Program - { - static void Main(string[] args) - { - IList y = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; - } - - static int ComputeCapacity() => 0; - static int TrueBranch() => 1; - static int FalseBranch() => 2; - } - """; - - var verifier = CompileAndVerify(source); - verifier.VerifyDiagnostics(); - - var compilation = (CSharpCompilation)verifier.Compilation; - var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Single()); - SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); - - var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); - ControlFlowGraphVerifier.VerifyGraph(compilation, """ - - Block[B0] - Entry - Statements (0) - Next (Regular) Block[B1] - Entering: {R1} - .locals {R1} - { - Locals: [System.Collections.Generic.IList y] - CaptureIds: [0] [1] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') - Value: - IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') - Instance Receiver: - null - Arguments(0) - Jump if False (Regular) to Block[B3] - IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') - Left: - IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') - Instance Receiver: - IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Next (Regular) Block[B2] - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') - Value: - IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') - Instance Receiver: - null - Arguments(0) - Next (Regular) Block[B4] - Block[B3] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') - Value: - IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') - Instance Receiver: - null - Arguments(0) - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B2] [B3] - Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') - Left: - ILocalReferenceOperation: y (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (CollectionExpression) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(Compu ... seBranch()]') - ConstructArguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Elements(1): - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') - Next (Regular) Block[B5] - Leaving: {R1} - } - Block[B5] - Exit - Predecessors: [B4] - Statements (0) - """, graph, symbol); - } - - [Fact] - public void ControlFlowBuilder() - { - string sourceA = """ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - - [CollectionBuilder(typeof(MyBuilder), "Create")] - class MyCollection : IEnumerable - { - public MyCollection(ReadOnlySpan items) { - } - IEnumerator IEnumerable.GetEnumerator() => throw null; - IEnumerator IEnumerable.GetEnumerator() => throw null; - } - class MyBuilder - { - public static MyCollection Create(int capacity, ReadOnlySpan items) - { - return new(items); - } - } - """; - string sourceB = """ - class Program - { - static void Main(string[] args) - { - MyCollection c = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; - } - - static int ComputeCapacity() => 0; - static int TrueBranch() => 1; - static int FalseBranch() => 2; - } - """; - var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80); - verifier.VerifyDiagnostics(); - - var compilation = (CSharpCompilation)verifier.Compilation; - var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); - SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); - - var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); - ControlFlowGraphVerifier.VerifyGraph(compilation, """ - Block[B0] - Entry - Statements (0) - Next (Regular) Block[B1] - Entering: {R1} - .locals {R1} - { - Locals: [MyCollection c] - CaptureIds: [0] [1] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') - Value: - IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') - Instance Receiver: - null - Arguments(0) - Jump if False (Regular) to Block[B3] - IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') - Left: - IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') - Instance Receiver: - IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') - Right: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Next (Regular) Block[B2] - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') - Value: - IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') - Instance Receiver: - null - Arguments(0) - Next (Regular) Block[B4] - Block[B3] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') - Value: - IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') - Instance Receiver: - null - Arguments(0) - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B2] [B3] - Statements (1) - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') - Left: - ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') - Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (CollectionExpression) - Operand: - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(Compu ... seBranch()]') - ConstructArguments(2): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(ComputeCapacity())') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') - InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Elements(1): - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') - Next (Regular) Block[B5] - Leaving: {R1} - } - Block[B5] - Exit - Predecessors: [B4] - Statements (0) - """, graph, symbol); - } } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 189f26cee363a..c3119e2d79335 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -2558,4 +2558,231 @@ class C Statements (0) """); } + + [Fact] + public void ControlFlow_ObjectCreation() + { + var source = """ + using System.Collections.Generic; + class Program + { + static void Main(string[] args) + { + IList y = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + + var verifier = CompileAndVerify(source); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Single()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [System.Collections.Generic.IList y] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') + Value: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') + Left: + ILocalReferenceOperation: y (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: 'y = [with(C ... seBranch()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.IList, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor(System.Int32 capacity)) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IList) (Syntax: '[with(Compu ... seBranch()]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + Next (Regular) Block[B5] + Leaving: {R1} + } + Block[B5] - Exit + Predecessors: [B4] + Statements (0) + """, graph, symbol); + } + + [Fact] + public void ControlFlow_Builder() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int capacity, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(ComputeCapacity()), args.Length == 0 ? TrueBranch() : FalseBranch()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') + Value: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(C ... seBranch()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(Compu ... seBranch()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(Compu ... seBranch()]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'ComputeCapacity()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'ComputeCapacity()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(ComputeCapacity())') + ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + Next (Regular) Block[B5] + Leaving: {R1} + } + Block[B5] - Exit + Predecessors: [B4] + Statements (0) + """, graph, symbol); + } } From 7a67ee4d9ff2a7e7a47203b1c82976454a81fcc7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 20:52:29 +0100 Subject: [PATCH 81/92] Renames --- .../Operations/CSharpOperationFactory.cs | 2 +- .../Semantics/CollectionExpressionTests.cs | 12 ++++---- ...ectionExpressionTests_WithElement_Extra.cs | 4 +-- ...ionTests_ICollectionExpressionOperation.cs | 30 +++++++++---------- .../Generated/OperationKind.Generated.cs | 4 +-- .../Generated/Operations.Generated.cs | 24 +++++++-------- .../Operations/ControlFlowGraphBuilder.cs | 8 ++--- .../Operations/OperationInterfaces.xml | 2 +- .../Core/Portable/PublicAPI.Unshipped.txt | 8 ++--- .../Compilation/ControlFlowGraphVerifier.cs | 2 +- .../Core/Compilation/OperationTreeVerifier.cs | 4 +-- .../Core/Compilation/TestOperationVisitor.cs | 4 +-- 12 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 0806523f37fff..73f6abd1d573c 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -323,7 +323,7 @@ public CSharpOperationFactory(SemanticModel semanticModel) }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); case BoundKind.CollectionBuilderElementsPlaceholder: - return new CollectionExpressionElementsOperation( + return new CollectionExpressionElementsPlaceholderOperation( _semanticModel, boundNode.Syntax, boundNode switch { diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 44244e363c7ab..ebf16c673848f 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16401,7 +16401,7 @@ .maxstack 1 ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -29121,7 +29121,7 @@ static MyCollection CreateCollection(MyCollection x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29187,7 +29187,7 @@ static IMyCollection CreateCollection(T a, T b) ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: IMyCollection) (Syntax: '[a, b]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29221,7 +29221,7 @@ static ImmutableArray Create(ImmutableArray x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29253,7 +29253,7 @@ static ImmutableArray Create(ImmutableArray x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -41088,7 +41088,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[new()]') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 42b7f9e5c824f..768c4135d9ca3 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1552,7 +1552,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): @@ -1573,7 +1573,7 @@ static void Main() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(t)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index c3119e2d79335..051681d9a3f94 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -1643,7 +1643,7 @@ void M() ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1676,7 +1676,7 @@ void M() ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1713,7 +1713,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1750,7 +1750,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1787,7 +1787,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1824,7 +1824,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1861,7 +1861,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1898,7 +1898,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2068,7 +2068,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2113,7 +2113,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2157,7 +2157,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2202,7 +2202,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2246,7 +2246,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2291,7 +2291,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2772,7 +2772,7 @@ static void Main(string[] args) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(ComputeCapacity())') - ICollectionExpressionElementsOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index 00fb0aaa3228b..c920786f9d2dd 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -285,7 +285,7 @@ public enum OperationKind CollectionExpression = 0x7f, /// Indicates an . Spread = 0x80, - /// Indicates an . - CollectionExpressionElements = 0x81, + /// Indicates an . + CollectionExpressionElementsPlaceholder = 0x81, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 92a2ae4303db4..4929c003a2745 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -4017,12 +4017,12 @@ public interface ISpreadOperation : IOperation /// /// This node is associated with the following operation kinds: /// - /// + /// /// /// This interface is reserved for implementation by its associated APIs. We reserve the right to /// change it in the future. /// - public interface ICollectionExpressionElementsOperation : IOperation + public interface ICollectionExpressionElementsPlaceholderOperation : IOperation { } #endregion @@ -10827,9 +10827,9 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i public override void Accept(OperationVisitor visitor) => visitor.VisitSpread(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitSpread(this, argument); } - internal sealed partial class CollectionExpressionElementsOperation : Operation, ICollectionExpressionElementsOperation + internal sealed partial class CollectionExpressionElementsPlaceholderOperation : Operation, ICollectionExpressionElementsPlaceholderOperation { - internal CollectionExpressionElementsOperation(SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) + internal CollectionExpressionElementsPlaceholderOperation(SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) : base(semanticModel, syntax, isImplicit) { Type = type; @@ -10840,9 +10840,9 @@ internal CollectionExpressionElementsOperation(SemanticModel? semanticModel, Syn internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) => (false, int.MinValue, int.MinValue); public override ITypeSymbol? Type { get; } internal override ConstantValue? OperationConstantValue => null; - public override OperationKind Kind => OperationKind.CollectionExpressionElements; - public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpressionElements(this); - public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpressionElements(this, argument); + public override OperationKind Kind => OperationKind.CollectionExpressionElementsPlaceholder; + public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionExpressionElementsPlaceholder(this); + public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionExpressionElementsPlaceholder(this, argument); } #endregion #region Cloner @@ -11467,10 +11467,10 @@ public override IOperation VisitSpread(ISpreadOperation operation, object? argum var internalOperation = (SpreadOperation)operation; return new SpreadOperation(Visit(internalOperation.Operand), internalOperation.ElementType, internalOperation.ElementConversionConvertible, internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit); } - public override IOperation VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, object? argument) + public override IOperation VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation, object? argument) { - var internalOperation = (CollectionExpressionElementsOperation)operation; - return new CollectionExpressionElementsOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + var internalOperation = (CollectionExpressionElementsPlaceholderOperation)operation; + return new CollectionExpressionElementsPlaceholderOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); } } #endregion @@ -11614,7 +11614,7 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitInlineArrayAccess(IInlineArrayAccessOperation operation) => DefaultVisit(operation); public virtual void VisitCollectionExpression(ICollectionExpressionOperation operation) => DefaultVisit(operation); public virtual void VisitSpread(ISpreadOperation operation) => DefaultVisit(operation); - public virtual void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) => DefaultVisit(operation); + public virtual void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11754,7 +11754,7 @@ public abstract partial class OperationVisitor public virtual TResult? VisitInlineArrayAccess(IInlineArrayAccessOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitCollectionExpression(ICollectionExpressionOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitSpread(ISpreadOperation operation, TArgument argument) => DefaultVisit(operation, argument); - public virtual TResult? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, TArgument argument) => DefaultVisit(operation, argument); + public virtual TResult? VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index fd4cdc3309c3d..c15c4b40ac626 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -1281,7 +1281,7 @@ private void AddStatement( || slot.operationOpt.Kind == OperationKind.DeclarationExpression || slot.operationOpt.Kind == OperationKind.Discard || slot.operationOpt.Kind == OperationKind.OmittedArgument - || slot.operationOpt.Kind == OperationKind.CollectionExpressionElements)); + || slot.operationOpt.Kind == OperationKind.CollectionExpressionElementsPlaceholder)); #endif if (statement == null) { @@ -1851,7 +1851,7 @@ private void SpillEvalStack() && operationOpt.Kind != OperationKind.DeclarationExpression && operationOpt.Kind != OperationKind.Discard && operationOpt.Kind != OperationKind.OmittedArgument - && operationOpt.Kind != OperationKind.CollectionExpressionElements) + && operationOpt.Kind != OperationKind.CollectionExpressionElementsPlaceholder) { // Here we need to decide what region should own the new capture. Due to the spilling operations occurred before, // we currently might be in a region that is not associated with the stack frame we are in, but it is one of its @@ -7443,10 +7443,10 @@ internal override IOperation VisitPlaceholder(IPlaceholderOperation operation, i return new PlaceholderOperation(operation.PlaceholderKind, semanticModel: null, operation.Syntax, operation.Type, IsImplicit(operation)); } - public override IOperation? VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation, int? argument) + public override IOperation? VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation, int? argument) { // Leave collection builder element placeholder alone. It itself doesn't affect flow control. - return new CollectionExpressionElementsOperation(semanticModel: null, operation.Syntax, operation.Type, operation.IsImplicit); + return new CollectionExpressionElementsPlaceholderOperation(semanticModel: null, operation.Syntax, operation.Type, operation.IsImplicit); } public override IOperation VisitConversion(IConversionOperation operation, int? captureIdForResult) diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 132d3ce8624e4..5ebaf5c361018 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3756,7 +3756,7 @@ - + Represents the elements of a collection expression as they are passed to some construction method diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index d8e4839133a8f..b9cf74b1900e5 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,8 +21,8 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool Microsoft.CodeAnalysis.IPropertySymbol.ReduceExtensionMember(Microsoft.CodeAnalysis.ITypeSymbol! receiverType) -> Microsoft.CodeAnalysis.IPropertySymbol? -Microsoft.CodeAnalysis.OperationKind.CollectionExpressionElements = 129 -> Microsoft.CodeAnalysis.OperationKind -Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation +Microsoft.CodeAnalysis.OperationKind.CollectionExpressionElementsPlaceholder = 129 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.ConstructArguments.get -> System.Collections.Immutable.ImmutableArray static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool @@ -53,5 +53,5 @@ const Microsoft.CodeAnalysis.WellKnownMemberNames.CheckedDecrementAssignmentOper const Microsoft.CodeAnalysis.WellKnownMemberNames.CheckedIncrementAssignmentOperatorName = "op_CheckedIncrementAssignment" -> string! const Microsoft.CodeAnalysis.WellKnownMemberNames.DecrementAssignmentOperatorName = "op_DecrementAssignment" -> string! const Microsoft.CodeAnalysis.WellKnownMemberNames.IncrementAssignmentOperatorName = "op_IncrementAssignment" -> string! -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElements(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation! operation) -> void -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElements(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsOperation! operation, TArgument argument) -> TResult? +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElementsPlaceholder(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation! operation) -> void +virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElementsPlaceholder(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation! operation, TArgument argument) -> TResult? diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index c397346209ef6..a0f121f55d34a 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -1979,7 +1979,7 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.InlineArrayAccess: case OperationKind.CollectionExpression: case OperationKind.Spread: - case OperationKind.CollectionExpressionElements: + case OperationKind.CollectionExpressionElementsPlaceholder: return true; } diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 60106a2da0794..c2154ed0e1bcf 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1143,9 +1143,9 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) Assert.True(operation.PlaceholderKind is PlaceholderKind.AggregationGroup); } - public override void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) + public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) { - LogString(nameof(ICollectionExpressionElementsOperation)); + LogString(nameof(ICollectionExpressionElementsPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); } diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index 16b5f1cfc5049..798e2fe800512 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -743,9 +743,9 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) Assert.Empty(operation.ChildOperations); } - public override void VisitCollectionExpressionElements(ICollectionExpressionElementsOperation operation) + public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) { - Assert.Equal(OperationKind.CollectionExpressionElements, operation.Kind); + Assert.Equal(OperationKind.CollectionExpressionElementsPlaceholder, operation.Kind); Assert.Empty(operation.ChildOperations); } From ad1e9c2df85da6ad984d6e4ae053755406a90d9b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 20 Nov 2025 20:57:01 +0100 Subject: [PATCH 82/92] Renames --- .../Semantics/CollectionExpressionTests.cs | 12 ++++---- ...ectionExpressionTests_WithElement_Extra.cs | 4 +-- ...ionTests_ICollectionExpressionOperation.cs | 30 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index ebf16c673848f..5bea3bbea6a05 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16401,7 +16401,7 @@ .maxstack 1 ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[1, 2, 3]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -29121,7 +29121,7 @@ static MyCollection CreateCollection(MyCollection x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29187,7 +29187,7 @@ static IMyCollection CreateCollection(T a, T b) ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: IMyCollection) (Syntax: '[a, b]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[a, b]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29221,7 +29221,7 @@ static ImmutableArray Create(ImmutableArray x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -29253,7 +29253,7 @@ static ImmutableArray Create(ImmutableArray x, int y) ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): @@ -41088,7 +41088,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[new()]') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: '[new()]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs index 768c4135d9ca3..e30e70579d6cd 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_Extra.cs @@ -1552,7 +1552,7 @@ static void Main() ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(), t]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): @@ -1573,7 +1573,7 @@ static void Main() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(t)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(t)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 051681d9a3f94..17a9265fa3bef 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -1643,7 +1643,7 @@ void M() ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1676,7 +1676,7 @@ void M() ICollectionExpressionOperation (3 elements, ConstructMethod: MyHashSet MyHashSetBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyHashSet) (Syntax: '[with(), 1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1713,7 +1713,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1750,7 +1750,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with()') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with()') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1787,7 +1787,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1824,7 +1824,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1861,7 +1861,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -1898,7 +1898,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capacity: 0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2068,7 +2068,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2113,7 +2113,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(0, null)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(0, null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2157,7 +2157,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2202,7 +2202,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(capaci ... arer: null)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(capaci ... arer: null)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2246,7 +2246,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2291,7 +2291,7 @@ void M() InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(compar ... apacity: 0)') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): @@ -2772,7 +2772,7 @@ static void Main(string[] args) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(ComputeCapacity())') - ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElements, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(ComputeCapacity())') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(1): From 524865e9ceed12f7d7cb5df79b38fcc5b5c8909e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 24 Nov 2025 20:35:49 +0100 Subject: [PATCH 83/92] Update semantic search --- .../ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index f03759ccffdf8..c264898d2f760 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -1777,6 +1777,7 @@ Microsoft.CodeAnalysis.OperationKind.Coalesce Microsoft.CodeAnalysis.OperationKind.CoalesceAssignment Microsoft.CodeAnalysis.OperationKind.CollectionElementInitializer Microsoft.CodeAnalysis.OperationKind.CollectionExpression +Microsoft.CodeAnalysis.OperationKind.CollectionExpressionElementsPlaceholder Microsoft.CodeAnalysis.OperationKind.CompoundAssignment Microsoft.CodeAnalysis.OperationKind.Conditional Microsoft.CodeAnalysis.OperationKind.ConditionalAccess @@ -2012,6 +2013,7 @@ Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_AddMethod Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_Arguments Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation.get_IsDynamic +Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructArguments Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_ConstructMethod @@ -2412,6 +2414,7 @@ Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCoalesce(Microsoft.CodeA Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCoalesceAssignment(Microsoft.CodeAnalysis.Operations.ICoalesceAssignmentOperation) Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionElementInitializer(Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation) Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpression(Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation) +Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionExpressionElementsPlaceholder(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation) Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCompoundAssignment(Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation) Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitConditional(Microsoft.CodeAnalysis.Operations.IConditionalOperation) Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitConditionalAccess(Microsoft.CodeAnalysis.Operations.IConditionalAccessOperation) @@ -2543,6 +2546,7 @@ Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCoalesce(Microsoft.Cod Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCoalesceAssignment(Microsoft.CodeAnalysis.Operations.ICoalesceAssignmentOperation,`0) Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCollectionElementInitializer(Microsoft.CodeAnalysis.Operations.ICollectionElementInitializerOperation,`0) Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCollectionExpression(Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation,`0) +Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCollectionExpressionElementsPlaceholder(Microsoft.CodeAnalysis.Operations.ICollectionExpressionElementsPlaceholderOperation,`0) Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitCompoundAssignment(Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation,`0) Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitConditional(Microsoft.CodeAnalysis.Operations.IConditionalOperation,`0) Microsoft.CodeAnalysis.Operations.OperationVisitor`2.VisitConditionalAccess(Microsoft.CodeAnalysis.Operations.IConditionalAccessOperation,`0) From 00490ae53683ca6220eb0d5455cee91825b80ce8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 26 Nov 2025 14:20:49 +0100 Subject: [PATCH 84/92] Update docs --- .../Generated/Operations.Generated.cs | 6 ++++- .../Operations/OperationInterfaces.xml | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index 4929c003a2745..bc115f7cff1d2 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3961,7 +3961,11 @@ public interface ICollectionExpressionOperation : IOperation /// /// /// If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. - /// Default values are supplied for optional arguments missing in source. + /// Default values are supplied for optional arguments missing in source. + /// If this is a collection builder method, this will include all arguments to the method, + /// except for the final ReadOnlySpan argument that receives the collection elements. + /// That final argument will be represented by an . + /// The actual elements passed to the creation method are contained in . /// ImmutableArray ConstructArguments { get; } /// diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 5ebaf5c361018..8a12768bb5000 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3699,15 +3699,19 @@ - - Arguments passed to to , if present. Arguments are in evaluation order. This can - be an empty array. Will never be default. If the arguments successfully bound, these will all be - ; otherwise, they can be any operation. - - - If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. - Default values are supplied for optional arguments missing in source. - + + Arguments passed to to , if present. Arguments are in evaluation order. This can + be an empty array. Will never be default. If the arguments successfully bound, these will all be + ; otherwise, they can be any operation. + + + If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. + Default values are supplied for optional arguments missing in source. + If this is a collection builder method, this will include all arguments to the method, + except for the final ReadOnlySpan argument that receives the collection elements. + That final argument will be represented by an . + The actual elements passed to the creation method are contained in . + From ab0d6f20c2616d3e923b9e0454606dfd1432de1b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 26 Nov 2025 14:21:25 +0100 Subject: [PATCH 85/92] remove links --- .../CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index 5bea3bbea6a05..fbcb9ef3ce6b4 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16395,8 +16395,6 @@ .maxstack 1 IL_000f: ret } """); - // We should extend IOperation conversions to represent IsCollectionExpression - // Tracked by https://github.com/dotnet/roslyn/issues/68826 VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection MyCollectionBuilder.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[1, 2, 3]') ConstructArguments(1): @@ -16474,8 +16472,6 @@ .maxstack 7 IL_0020: ret } """); - // We should extend IOperation conversions to represent IsCollectionExpression - // Tracked by https://github.com/dotnet/roslyn/issues/68826 VerifyOperationTreeForTest(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[][]) (Syntax: '[[1], [2]]') From f19c06d3406d676452e90db91e1c1703d010f487 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Dec 2025 23:56:53 +0100 Subject: [PATCH 86/92] Update src/Compilers/Core/Portable/Operations/OperationInterfaces.xml Co-authored-by: Fred Silberberg --- .../Operations/OperationInterfaces.xml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 8a12768bb5000..e36579b9311d0 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3699,19 +3699,19 @@ - - Arguments passed to to , if present. Arguments are in evaluation order. This can - be an empty array. Will never be default. If the arguments successfully bound, these will all be - ; otherwise, they can be any operation. - - - If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. - Default values are supplied for optional arguments missing in source. - If this is a collection builder method, this will include all arguments to the method, - except for the final ReadOnlySpan argument that receives the collection elements. - That final argument will be represented by an . - The actual elements passed to the creation method are contained in . - + + Arguments passed to to , if present. Arguments are in evaluation order. This can + be an empty array. Will never be . If the arguments successfully bound, these will all be + ; otherwise, they can be any operation. + + + If the invocation is in its expanded form, then params/ParamArray arguments would be collected into arrays. + Default values are supplied for optional arguments missing in source. + If this is a collection builder method, this will include all arguments to the method, + except for the final ReadOnlySpan argument that receives the collection elements. + That final argument will be represented by an . + The actual elements passed to the creation method are contained in . + From ebcca9313fa7ae0b2e8d5ec2573f82ab649dfbb8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 3 Dec 2025 23:59:41 +0100 Subject: [PATCH 87/92] Remove unnecessary using --- .../CSharp/Portable/BoundTree/BoundCollectionExpression.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 242c2d60ec548..3770fb1f1becc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp { From d1c8c442e501b74a9ab25f58896171c7c55f83b1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2025 00:00:22 +0100 Subject: [PATCH 88/92] Already debug --- .../CSharp/Portable/BoundTree/BoundCollectionExpression.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 3770fb1f1becc..3a78d408bb10e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -10,7 +10,6 @@ internal partial class BoundCollectionExpression { private partial void Validate() { -#if DEBUG var collectionCreation = this.CollectionCreation; while (collectionCreation is BoundConversion conversion) collectionCreation = conversion.Operand; @@ -35,7 +34,6 @@ or BoundNewT Debug.Assert(this.CollectionBuilderMethod is not null); Debug.Assert(this.CollectionBuilderElementsPlaceholder is not null); } -#endif } } From 681b216bf13fbae7e3018d3426542b991737b15f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2025 00:01:08 +0100 Subject: [PATCH 89/92] Update src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs Co-authored-by: Fred Silberberg --- .../CSharp/Portable/Operations/CSharpOperationFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 73f6abd1d573c..b4eaa80ef177d 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -706,7 +706,7 @@ private IAnonymousObjectCreationOperation CreateBoundAnonymousObjectCreationExpr } private static bool CanDeriveObjectCreationExpressionArguments(BoundObjectCreationExpression boundObjectCreationExpression) - => boundObjectCreationExpression.ResultKind != LookupResultKind.OverloadResolutionFailure && boundObjectCreationExpression.Constructor.OriginalDefinition is not ErrorMethodSymbol; + => boundObjectCreationExpression is { ResultKind: not LookupResultKind.OverloadResolutionFailure, Constructor.OriginalDefinition: not ErrorMethodSymbol }; private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreationExpression boundObjectCreationExpression) { From 2b5b8c1610783ff11693bcfc6f10adc3d93d9cf5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2025 00:04:36 +0100 Subject: [PATCH 90/92] Revert --- src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index c2154ed0e1bcf..719323071b969 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1140,7 +1140,7 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) { LogString(nameof(IPlaceholderOperation)); LogCommonPropertiesAndNewLine(operation); - Assert.True(operation.PlaceholderKind is PlaceholderKind.AggregationGroup); + Assert.Equal(PlaceholderKind.AggregationGroup, operation.PlaceholderKind); } public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) From 2ab435a9b669b5a34e6a055c1c7681c1692a50f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2025 00:19:50 +0100 Subject: [PATCH 91/92] Add tests --- ...ionTests_ICollectionExpressionOperation.cs | 774 +++++++++++++++++- 1 file changed, 773 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs index 17a9265fa3bef..079debe7c53d7 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -2662,7 +2662,7 @@ static void Main(string[] args) } [Fact] - public void ControlFlow_Builder() + public void ControlFlow_BuilderA() { string sourceA = """ using System; @@ -2785,4 +2785,776 @@ static void Main(string[] args) Statements (0) """, graph, symbol); } + + [Fact] + public void ControlFlow_BuilderB() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int capacity, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(args.Length == 0 ? TrueBranch() : FalseBranch()), ComputeCapacity()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... Capacity()]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... Capacity()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(args. ... Capacity()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(args. ... Capacity()]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'args.Length ... lseBranch()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(args.L ... seBranch())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(args.L ... seBranch())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B5] + Leaving: {R1} + } + Block[B5] - Exit + Predecessors: [B4] + Statements (0) + """, graph, symbol); + } + + [Fact] + public void ControlFlow_BuilderC() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int capacity, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(args.Length == 0 ? TrueBranch() : FalseBranch()), args.Length == 1 ? FalseBranch() : TrueBranch()]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Jump if False (Regular) to Block[B6] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 1') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B6] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B7] - Block + Predecessors: [B5] [B6] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... ueBranch()]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... ueBranch()]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(args. ... ueBranch()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 capacity, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(args. ... ueBranch()]') + ConstructArguments(2): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: capacity) (OperationKind.Argument, Type: null) (Syntax: 'args.Length ... lseBranch()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(args.L ... seBranch())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(args.L ... seBranch())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... rueBranch()') + Next (Regular) Block[B8] + Leaving: {R1} + } + Block[B8] - Exit + Predecessors: [B7] + Statements (0) + """, graph, symbol); + } + + [Fact] + public void ControlFlow_BuilderD() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int arg1, int arg2, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(args.Length == 0 ? TrueBranch() : FalseBranch(), args.Length == 1 ? FalseBranch() : TrueBranch())]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Jump if False (Regular) to Block[B6] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 1') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B6] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B7] - Block + Predecessors: [B5] [B6] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... eBranch())]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... eBranch())]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(args. ... eBranch())]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 arg1, System.Int32 arg2, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(args. ... eBranch())]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg1) (OperationKind.Argument, Type: null) (Syntax: 'args.Length ... lseBranch()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg2) (OperationKind.Argument, Type: null) (Syntax: 'args.Length ... rueBranch()') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... rueBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(args.L ... ueBranch())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(args.L ... ueBranch())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(0) + Next (Regular) Block[B8] + Leaving: {R1} + } + Block[B8] - Exit + Predecessors: [B7] + Statements (0) + """, graph, symbol); + } + + [Fact] + public void ControlFlow_BuilderE() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int arg1, int arg2, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(arg2: args.Length == 0 ? TrueBranch() : FalseBranch(), arg1: args.Length == 1 ? FalseBranch() : TrueBranch())]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Jump if False (Regular) to Block[B6] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 1') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B6] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B7] - Block + Predecessors: [B5] [B6] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... eBranch())]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... eBranch())]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(arg2: ... eBranch())]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 arg1, System.Int32 arg2, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(arg2: ... eBranch())]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg2) (OperationKind.Argument, Type: null) (Syntax: 'arg2: args. ... lseBranch()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg1) (OperationKind.Argument, Type: null) (Syntax: 'arg1: args. ... rueBranch()') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... rueBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(arg2: ... ueBranch())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(arg2: ... ueBranch())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(0) + Next (Regular) Block[B8] + Leaving: {R1} + } + Block[B8] - Exit + Predecessors: [B7] + Statements (0) + """, graph, symbol); + } + + [Fact] + public void ControlFlow_BuilderF() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyBuilder), "Create")] + class MyCollection : IEnumerable + { + public MyCollection(ReadOnlySpan items) { + } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class MyBuilder + { + public static MyCollection Create(int arg1, int arg2, ReadOnlySpan items) + { + return new(items); + } + } + """; + string sourceB = """ + class Program + { + static void Main(string[] args) + { + MyCollection c = [with(arg2: args.Length == 0 ? TrueBranch() : FalseBranch(), arg1: args.Length == 1 ? FalseBranch() : TrueBranch()), args.Length == 2 ? ComputeCapacity() : (ComputeCapacity() + 1)]; + } + + static int ComputeCapacity() => 0; + static int TrueBranch() => 1; + static int FalseBranch() => 2; + } + """; + var verifier = CompileAndVerify([sourceA, sourceB], targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); + verifier.VerifyDiagnostics(); + + var compilation = (CSharpCompilation)verifier.Compilation; + var semanticModel = compilation.GetSemanticModel(compilation.SyntaxTrees.Last()); + SyntaxNode root = semanticModel.SyntaxTree.GetRoot(); + + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(root.DescendantNodes().OfType().Single(), semanticModel); + ControlFlowGraphVerifier.VerifyGraph(compilation, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + Locals: [MyCollection c] + CaptureIds: [0] [1] [2] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 0') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Jump if False (Regular) to Block[B6] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 1') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'FalseBranch()') + Value: + IInvocationOperation (System.Int32 Program.FalseBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'FalseBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B6] - Block + Predecessors: [B4] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'TrueBranch()') + Value: + IInvocationOperation (System.Int32 Program.TrueBranch()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'TrueBranch()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B7] + Block[B7] - Block + Predecessors: [B5] [B6] + Statements (0) + Jump if False (Regular) to Block[B9] + IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'args.Length == 2') + Left: + IPropertyReferenceOperation: System.Int32 System.Array.Length { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'args.Length') + Instance Receiver: + IParameterReferenceOperation: args (OperationKind.ParameterReference, Type: System.String[]) (Syntax: 'args') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + Next (Regular) Block[B8] + Block[B8] - Block + Predecessors: [B7] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity()') + Value: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Next (Regular) Block[B10] + Block[B9] - Block + Predecessors: [B7] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'ComputeCapacity() + 1') + Value: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.Int32) (Syntax: 'ComputeCapacity() + 1') + Left: + IInvocationOperation (System.Int32 Program.ComputeCapacity()) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'ComputeCapacity()') + Instance Receiver: + null + Arguments(0) + Right: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + Next (Regular) Block[B10] + Block[B10] - Block + Predecessors: [B8] [B9] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... ity() + 1)]') + Left: + ILocalReferenceOperation: c (IsDeclaration: True) (OperationKind.LocalReference, Type: MyCollection, IsImplicit) (Syntax: 'c = [with(a ... ity() + 1)]') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[with(arg2: ... ity() + 1)]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection MyBuilder.Create(System.Int32 arg1, System.Int32 arg2, System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[with(arg2: ... ity() + 1)]') + ConstructArguments(3): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg2) (OperationKind.Argument, Type: null) (Syntax: 'arg2: args. ... lseBranch()') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... lseBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: arg1) (OperationKind.Argument, Type: null) (Syntax: 'arg1: args. ... rueBranch()') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... rueBranch()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'with(arg2: ... ueBranch())') + ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan, IsImplicit) (Syntax: 'with(arg2: ... ueBranch())') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Elements(1): + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'args.Length ... city() + 1)') + Next (Regular) Block[B11] + Leaving: {R1} + } + Block[B11] - Exit + Predecessors: [B10] + Statements (0) + """, graph, symbol); + } } From 93f6a3af8154552c137f2eb98e17d613424277bd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 4 Dec 2025 09:19:41 +0100 Subject: [PATCH 92/92] update gen --- src/Compilers/Core/Portable/Generated/Operations.Generated.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index bc115f7cff1d2..5b5a8209592a5 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3956,7 +3956,7 @@ public interface ICollectionExpressionOperation : IOperation IMethodSymbol? ConstructMethod { get; } /// /// Arguments passed to to , if present. Arguments are in evaluation order. This can - /// be an empty array. Will never be default. If the arguments successfully bound, these will all be + /// be an empty array. Will never be . If the arguments successfully bound, these will all be /// ; otherwise, they can be any operation. /// ///