diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index d8bd1c33745bf..2956099c551db 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 de24a28776f47..c9653e190def7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -841,7 +841,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, @@ -1153,6 +1153,7 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( ErrorCode.ERR_CollectionArgumentsNotSupportedForType, _node.WithElement.Syntax.GetFirstToken().GetLocation(), _targetType); + return null; } return CreateCollectionExpression(collectionTypeKind, elements); @@ -1160,6 +1161,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, @@ -1176,13 +1186,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. @@ -1257,7 +1260,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; @@ -1340,7 +1343,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; @@ -1371,8 +1374,9 @@ static BoundCollectionExpressionSpreadElement bindSpreadElement( invokedAsExtensionMethod: false, argsToParamsOpt: argsToParams, defaultArguments: projectionCall.DefaultArguments, - resultKind: LookupResultKind.Viable, - type: collectionBuilderMethod.ReturnType) + resultKind: projectionCall.ResultKind, + type: collectionBuilderMethod.ReturnType, + hasErrors: projectionCall.HasErrors) { WasCompilerGenerated = @this._node.WithElement is null, }; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs index 32b08d834ec87..3a78d408bb10e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundCollectionExpression.cs @@ -10,6 +10,24 @@ internal partial class BoundCollectionExpression { private partial void Validate() { + 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); + + if (collectionCreation is BoundCall boundCall) + { + Debug.Assert( + boundCall.Arguments is [.., BoundCollectionBuilderElementsPlaceholder placeHolder] && + placeHolder == this.CollectionBuilderElementsPlaceholder); + } + if (this.CollectionTypeKind == CollectionExpressionTypeKind.CollectionBuilder) { Debug.Assert(this.CollectionCreation is not null); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 2b99950cafcfe..81ede01ce914b 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 9c9ba850cd440..54724249885cc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -181,6 +181,10 @@ + + + @@ -1942,7 +1946,7 @@ - + @@ -1960,9 +1964,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 20512d8e793ab..090c2d73d578a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3708,6 +3708,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 769c5a10c0c2e..3f4fd7072c97c 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) @@ -6496,7 +6525,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()) { @@ -6523,7 +6552,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; } @@ -6531,7 +6560,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)) { @@ -8973,6 +9002,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: @@ -9430,6 +9461,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); @@ -9667,6 +9699,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); @@ -9924,6 +9957,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) { @@ -11011,6 +11045,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); @@ -12025,7 +12064,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); @@ -12693,6 +12732,18 @@ public NullabilityRewriter(ImmutableDictionary elements = this.VisitList(node.Elements); BoundCollectionExpression updatedNode; @@ -15346,6 +15397,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 4c46478c895f3..b4eaa80ef177d 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 { @@ -323,6 +322,14 @@ public CSharpOperationFactory(SemanticModel semanticModel) _ => null }; return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit); + case BoundKind.CollectionBuilderElementsPlaceholder: + return new CollectionExpressionElementsPlaceholderOperation( + _semanticModel, boundNode.Syntax, + boundNode switch + { + BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(), + _ => null + }, boundNode.WasCompilerGenerated); case BoundKind.UnconvertedInterpolatedString: case BoundKind.UnconvertedConditionalOperator: case BoundKind.UnconvertedSwitchExpression: @@ -698,17 +705,17 @@ private IAnonymousObjectCreationOperation CreateBoundAnonymousObjectCreationExpr return new AnonymousObjectCreationOperation(initializers, _semanticModel, syntax, type, isImplicit); } + private static bool CanDeriveObjectCreationExpressionArguments(BoundObjectCreationExpression boundObjectCreationExpression) + => boundObjectCreationExpression is { ResultKind: not LookupResultKind.OverloadResolutionFailure, Constructor.OriginalDefinition: not ErrorMethodSymbol }; + 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 (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); @@ -730,7 +737,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) @@ -1222,20 +1237,16 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundCollectionExpression expr) { - SyntaxNode syntax = expr.Syntax; - ITypeSymbol? collectionType = expr.GetPublicTypeSymbol(); - bool isImplicit = expr.WasCompilerGenerated; - IMethodSymbol? constructMethod = getConstructMethod((CSharpCompilation)_semanticModel.Compilation, expr).GetPublicSymbol(); - ImmutableArray elements = expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr); return new CollectionExpressionOperation( - constructMethod, - 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) { @@ -1253,6 +1264,45 @@ private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundColl throw ExceptionUtilities.UnexpectedValue(expr.CollectionTypeKind); } } + + static ImmutableArray getConstructArguments( + CSharpOperationFactory @this, BoundCollectionExpression expr) + { + var collectionCreation = expr.CollectionCreation; + while (collectionCreation is BoundConversion conversion) + collectionCreation = conversion.Operand; + + 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); + 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. + return boundCall.IsErroneousNode + ? @this.CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren) + : ImmutableArray.CastUp(@this.DeriveArguments(collectionCreation)); + } + + 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 []; + } } private IOperation CreateBoundCollectionExpressionElement(BoundCollectionExpression expr, BoundNode element) diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs index dc6b684d24ba0..fbcb9ef3ce6b4 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs @@ -16395,11 +16395,13 @@ .maxstack 1 IL_000f: ret } """); - // 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]') + 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): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') @@ -16470,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]]') @@ -29108,14 +29108,18 @@ 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]') 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]') + 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): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: @@ -29170,14 +29174,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]') + 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): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') @@ -29205,9 +29213,13 @@ 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]') + 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): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: @@ -29222,8 +29234,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] @@ -29236,6 +29247,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]') + 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): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: @@ -41066,6 +41082,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()]') + 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): 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_ArraysAndSpans.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests_WithElement_ArraysAndSpans.cs index f57bd6b6f41af..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; @@ -518,10 +520,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 +561,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)); } @@ -586,13 +582,21 @@ 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'. + 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")] 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..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,8 +393,17 @@ 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') + 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) + 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') """); } @@ -455,6 +464,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]') + 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) + 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] @@ -468,6 +486,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]') + 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) + 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] @@ -523,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 @@ -534,8 +559,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: @@ -546,13 +592,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]') + 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) + 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 c5f0a55f89ab2..e30e70579d6cd 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,31 +308,33 @@ 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) { - {{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), + // (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(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] @@ -915,19 +920,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 +943,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 +1005,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)); } @@ -1530,28 +1544,41 @@ 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()') + 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): + 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]') - Elements(1): + 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)') + 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): + IParameterReferenceOperation: t (OperationKind.ParameterReference, Type: T) (Syntax: 't') """); - } [Fact] @@ -1586,7 +1613,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: '{ ... }') + 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];') @@ -1615,6 +1642,11 @@ 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]') + 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) + 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') @@ -1657,6 +1689,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]') + 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) + 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') @@ -3952,10 +3989,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] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs new file mode 100644 index 0000000000000..079debe7c53d7 --- /dev/null +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICollectionExpressionOperation.cs @@ -0,0 +1,3560 @@ +// 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.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; +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 IOperationTests_ICollectionExpressionOperation : 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!; + } + """; + + 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 void Add(int item) { } + + IEnumerator IEnumerable.GetEnumerator() => null!; + IEnumerator IEnumerable.GetEnumerator() => null; + } + + class MyHashSetBuilder + { + public static MyHashSet Create(int capacity = 42, ReadOnlySpan items = default) => null!; + } + """; + + [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') + """); + 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] + 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]') + 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') + """); + 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] + 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]') + 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') + """); + 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] + 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') + """); + 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.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) + """); + } + + [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]') + 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') + """); + 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.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) + """); + } + + [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]') + 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') + """); + 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.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) + """); + } + + [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') + """); + 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.Collections.Generic.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + 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.{{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') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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]') + 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') + """); + 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.Collections.Generic.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + 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.{{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') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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]') + 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') + """); + 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.Collections.Generic.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + 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.{{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') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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') + """); + + 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.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsImplicit) (Syntax: 'a = [with(), 1, 2, 3]') + Right: + 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: 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') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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') + """); + + 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.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsImplicit) (Syntax: 'a = [with(0), 1, 2, 3]') + Right: + 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.{{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') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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') + """); + + 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.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsImplicit) (Syntax: 'a = [with(c ... ), 1, 2, 3]') + Right: + 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.{{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') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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]') + 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') + """); + + 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.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(u ... ), 1, 2, 3]') + Right: + 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.{{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') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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]') + 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') + """); + + 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.{{typeName}} a] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + 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.{{typeName}}, IsInvalid, IsImplicit) (Syntax: 'a = [with(0 ... ), 1, 2, 3]') + Right: + 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.{{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') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') + Next (Regular) Block[B2] + Leaving: {R1} + } + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); + } + + [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') + """); + + 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] + 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') + """); + + 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) + """); + } + + [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(), """ + 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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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') + """); + + 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] + 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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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]') + 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') + """); + + 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) + """); + } + + [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]') + 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') + """); + + 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) + """); + } + + [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]') + ConstructArguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, 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): + 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]; + 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()') + 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): + 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] + 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(), """ + 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()') + 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): + 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]; + 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()') + 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): + 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] + 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(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)') + 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): + 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]; + 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)') + 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): + 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] + 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(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)') + 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): + 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]; + 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)') + 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): + 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] + 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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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(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) + 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)') + 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): + 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]; + 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)') + 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): + 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] + 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(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) + 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)') + 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): + 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]; + 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)') + 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): + 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] + 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(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) + 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)') + 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): + 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]; + 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)') + 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): + 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] + 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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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') + """); + + 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) + """); + } + + [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_BuilderA() + { + 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())') + 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): + 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_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); + } +} 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 @@ + diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index be6e5990ace6a..c920786f9d2dd 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -285,5 +285,7 @@ public enum OperationKind CollectionExpression = 0x7f, /// Indicates an . Spread = 0x80, + /// 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 bc88d5fe9eff7..5b5a8209592a5 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3955,6 +3955,20 @@ 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 . 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 . + /// + ImmutableArray ConstructArguments { get; } + /// /// Collection expression elements. /// /// If the element is an expression, the entry is the expression, with a conversion to @@ -3997,6 +4011,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 ICollectionExpressionElementsPlaceholderOperation : IOperation + { + } #endregion #region Implementations @@ -10672,21 +10704,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 constructArguments, ImmutableArray elements, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) : base(semanticModel, syntax, isImplicit) { ConstructMethod = constructMethod; + ConstructArguments = SetParentOperation(constructArguments, this); Elements = SetParentOperation(elements, this); Type = type; } public IMethodSymbol? ConstructMethod { get; } + public ImmutableArray ConstructArguments { get; } public ImmutableArray Elements { get; } internal override int ChildOperationsCount => + ConstructArguments.Length + Elements.Length; internal override IOperation GetCurrent(int slot, int index) => slot switch { - 0 when index < Elements.Length + 0 when index < ConstructArguments.Length + => ConstructArguments[index], + 1 when index < Elements.Length => Elements[index], _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), }; @@ -10695,13 +10732,18 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previ switch (previousSlot) { case -1: - if (!Elements.IsEmpty) return (true, 0, 0); + if (!ConstructArguments.IsEmpty) return (true, 0, 0); else goto case 0; - case 0 when previousIndex + 1 < Elements.Length: + case 0 when previousIndex + 1 < ConstructArguments.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 +10753,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 (!ConstructArguments.IsEmpty) return (true, 0, ConstructArguments.Length - 1); else goto case 0; case 0 when previousIndex > 0: return (true, 0, previousIndex - 1); @@ -10784,6 +10831,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 CollectionExpressionElementsPlaceholderOperation : Operation, ICollectionExpressionElementsPlaceholderOperation + { + internal CollectionExpressionElementsPlaceholderOperation(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.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 internal sealed partial class OperationCloner : OperationVisitor @@ -11400,13 +11464,18 @@ 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.ConstructArguments), VisitArray(internalOperation.Elements), 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 VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation, object? argument) + { + var internalOperation = (CollectionExpressionElementsPlaceholderOperation)operation; + return new CollectionExpressionElementsPlaceholderOperation(internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); + } } #endregion @@ -11549,6 +11618,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 VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11688,6 +11758,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? 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 753a206354748..c15c4b40ac626 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.CollectionExpressionElementsPlaceholder)); #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.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 @@ -6542,6 +6544,23 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize public override IOperation? VisitCollectionExpression(ICollectionExpressionOperation operation, int? argument) { EvalStackFrame frame = 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"); + + // 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) + { + VisitAndPushArray(operation.ConstructArguments); + } + else + { + VisitAndPushArguments(arguments, instancePushed: false); + } + var elements = VisitArray( operation.Elements, unwrapper: static (IOperation element) => @@ -6562,14 +6581,19 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize IsImplicit(spread)) : operation; }); - PopStackFrame(frame); - return new CollectionExpressionOperation( + + var creationArguments = arguments.IsDefault + ? PopArray(operation.ConstructArguments) + : ImmutableArray.CastUp(PopArray(arguments, RewriteArgumentFromArray)); + + 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) @@ -7419,6 +7443,12 @@ internal override IOperation VisitPlaceholder(IPlaceholderOperation operation, i return new PlaceholderOperation(operation.PlaceholderKind, semanticModel: null, operation.Syntax, operation.Type, IsImplicit(operation)); } + public override IOperation? VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation, int? argument) + { + // Leave collection builder element placeholder alone. It itself doesn't affect flow control. + return new CollectionExpressionElementsPlaceholderOperation(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/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index f1d3f1af38be3..e36579b9311d0 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,23 @@ + + + + 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 . + + + @@ -3743,4 +3760,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. + + + diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index fc5b0a225448b..b9cf74b1900e5 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -21,6 +21,9 @@ 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.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 Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? @@ -50,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.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 8384b3c4572bc..a0f121f55d34a 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -1979,6 +1979,7 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.InlineArrayAccess: case OperationKind.CollectionExpression: case OperationKind.Spread: + case OperationKind.CollectionExpressionElementsPlaceholder: return true; } diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 8c97ecf3362ba..719323071b969 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -1143,6 +1143,12 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) Assert.Equal(PlaceholderKind.AggregationGroup, operation.PlaceholderKind); } + public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) + { + LogString(nameof(ICollectionExpressionElementsPlaceholderOperation)); + LogCommonPropertiesAndNewLine(operation); + } + public override void VisitUnaryOperator(IUnaryOperation operation) { LogString(nameof(IUnaryOperation)); @@ -1615,6 +1621,9 @@ public override void VisitCollectionExpression(ICollectionExpressionOperation op LogString(")"); LogCommonPropertiesAndNewLine(operation); + if (operation.ConstructArguments.Length > 0) + VisitArray(operation.ConstructArguments, nameof(operation.ConstructArguments), 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..798e2fe800512 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.ConstructArguments, .. operation.Elements], operation.ChildOperations); } public override void VisitSpread(ISpreadOperation operation) @@ -743,6 +743,12 @@ internal override void VisitPlaceholder(IPlaceholderOperation operation) Assert.Empty(operation.ChildOperations); } + public override void VisitCollectionExpressionElementsPlaceholder(ICollectionExpressionElementsPlaceholderOperation operation) + { + Assert.Equal(OperationKind.CollectionExpressionElementsPlaceholder, operation.Kind); + Assert.Empty(operation.ChildOperations); + } + public override void VisitUnaryOperator(IUnaryOperation operation) { Assert.Equal(OperationKind.UnaryOperator, operation.Kind); diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index c3da974bae4db..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,7 +2013,9 @@ 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 Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation.get_Elements Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation @@ -2411,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) @@ -2542,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)