diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 21a6b0e2e4cb1..76b8741267d6e 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -35,7 +35,7 @@ public class C *Conversion* of a collection expression to a `struct` or `class` that implements `System.Collections.IEnumerable` and *does not* have a `CollectionBuilderAttribute` requires the target type to have an accessible constructor that can be called with no arguments and, if the collection expression is not empty, the target type must have an accessible `Add` method -that can be called with a single argument of [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/statements.md#1395-the-foreach-statement) of the target type. +that can be called with a single argument. Previously, the constructor and `Add` methods were required for *construction* of the collection instance but not for *conversion*. That meant the following call was ambiguous since both `char[]` and `string` were valid target types for the collection expression. @@ -47,24 +47,6 @@ static void Print(char[] arg) { } static void Print(string arg) { } ``` -Previously, the collection expression in `y = [1, 2, 3]` was allowed since construction only requires an applicable `Add` method for each element expression. -The collection expression is now an error because of the conversion requirement for an `Add` method than be called with an argument of the iteration type `object`. -```csharp -// ok: Add is not required for empty collection -MyCollection x = []; - -// error CS9215: Collection expression type must have an applicable instance or extension method 'Add' -// that can be called with an argument of iteration type 'object'. -// The best overloaded method is 'MyCollection.Add(int)'. -MyCollection y = [1, 2, 3]; - -class MyCollection : IEnumerable -{ - public void Add(int i) { ... } - IEnumerator IEnumerable.GetEnumerator() { ... } -} -``` - ## `ref` arguments can be passed to `in` parameters ***Introduced in Visual Studio 2022 version 17.8p2*** diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs index 37e3bdc1f4dac..9236edbf08487 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs @@ -5270,16 +5270,6 @@ public void Add(string s) { } { OutputKind = OutputKind.DynamicallyLinkedLibrary, }, - FixedState = - { - ExpectedDiagnostics = - { - // /0/Test0.cs(6,26): error CS1503: Argument 1: cannot convert from 'object' to 'string' - DiagnosticResult.CompilerError("CS1503").WithSpan(6, 26, 6, 36).WithArguments("1", "object", "string"), - // /0/Test0.cs(6,26): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(string)'. - DiagnosticResult.CompilerError("CS9215").WithSpan(6, 26, 6, 36).WithArguments("object", "MyCollection.Add(string)"), - } - } }.RunAsync(); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 919527d75cd9e..002b0a25e6cb1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -368,7 +368,7 @@ private BoundIndexerAccess BindIndexerDefaultArgumentsAndParamsCollection(BoundI } } - BindDefaultArgumentsAndParamsCollection(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics); if (namesBuilder is object) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index fb59362df2573..1170b0c98b125 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -181,7 +181,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a Binder attributeArgumentBinder = this.WithAdditionalFlags(BinderFlags.AttributeArgument); AnalyzedAttributeArguments analyzedArguments = attributeArgumentBinder.BindAttributeArguments(argumentListOpt, attributeTypeForBinding, diagnostics); - ImmutableArray argsToParamsOpt = default; + ImmutableArray argsToParamsOpt; bool expanded = false; BitVector defaultArguments = default; MethodSymbol? attributeConstructor = null; @@ -191,6 +191,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a boundConstructorArguments = analyzedArguments.ConstructorArguments.Arguments.SelectAsArray( static (arg, attributeArgumentBinder) => attributeArgumentBinder.BindToTypeForErrorRecovery(arg), attributeArgumentBinder); + argsToParamsOpt = default; } else { @@ -210,12 +211,15 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argsToParamsOpt); + } + else + { + argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; } attributeConstructor = memberResolutionResult.Member; expanded = memberResolutionResult.Resolution == MemberResolutionKind.ApplicableInExpandedForm; - argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; if (!found) { @@ -229,7 +233,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a } else { - attributeArgumentBinder.BindDefaultArgumentsAndParamsCollection( + attributeArgumentBinder.BindDefaultArguments( node, attributeConstructor.Parameters, analyzedArguments.ConstructorArguments.Arguments, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 6b07dc19faac5..de77f9ff5d4cd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -944,7 +944,7 @@ private bool HasParamsCollectionTypeInProgress(NamedTypeSymbol toCheck) return false; } - internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, TypeSymbol elementType, out ImmutableArray addMethods, BindingDiagnosticBag diagnostics) + internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, out ImmutableArray addMethods, BindingDiagnosticBag diagnostics) { Debug.Assert(!targetType.IsDynamic()); @@ -958,7 +958,12 @@ internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, Type } var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; - var elementPlaceholder = new BoundValuePlaceholder(syntax, elementType) { WasCompilerGenerated = true }; + + // For the element, we create a dynamic argument and will be forcing overload resolution to convert it to any type. + // This way we are going to do most of the work in terms of determining applicability of 'Add' method candidates + // in overload resolution. + var elementPlaceholder = new BoundValuePlaceholder(syntax, Compilation.DynamicType) { WasCompilerGenerated = true }; + var addMethodBinder = WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod | BinderFlags.CollectionExpressionConversionValidation); if (namedType is not null) @@ -1047,19 +1052,6 @@ static bool bindInvocationExpression( diagnostics, out addMethods); } - // This is what BindDynamicInvocation is doing in terms of reporting diagnostics and detecting a failure - static bool bindDynamicInvocation( - Binder addMethodBinder, - SyntaxNode node, - AnalyzedArguments arguments, - BindingDiagnosticBag diagnostics) - { - ImmutableArray argArray = addMethodBinder.BuildArgumentsForDynamicInvocation(arguments, diagnostics); - var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); - - return !ReportBadDynamicArguments(node, argArray, refKindsArray, diagnostics, queryClause: null); - } - // This is what BindMethodGroupInvocation is doing in terms of reporting diagnostics and detecting a failure static bool bindMethodGroupInvocation( Binder addMethodBinder, @@ -1070,12 +1062,15 @@ static bool bindMethodGroupInvocation( BindingDiagnosticBag diagnostics, out ImmutableArray addMethods) { + Debug.Assert(methodGroup.ReceiverOpt is not null); + Debug.Assert(methodGroup.ReceiverOpt.Type is not null); + bool result; CompoundUseSiteInfo useSiteInfo = addMethodBinder.GetNewCompoundUseSiteInfo(diagnostics); var resolution = addMethodBinder.ResolveMethodGroup( methodGroup, expression, WellKnownMemberNames.CollectionInitializerAddMethodName, analyzedArguments, useSiteInfo: ref useSiteInfo, - options: (analyzedArguments.HasDynamicArgument ? OverloadResolution.Options.DynamicResolution : OverloadResolution.Options.None)); + options: OverloadResolution.Options.DynamicResolution | OverloadResolution.Options.DynamicConvertsToAnything); diagnostics.Add(expression, useSiteInfo); @@ -1100,10 +1095,11 @@ static bool bindMethodGroupInvocation( } else { + Debug.Assert(resolution.AnalyzedArguments.HasDynamicArgument); + // If overload resolution found one or more applicable methods and at least one argument // was dynamic then treat this as a dynamic call. - if (resolution.AnalyzedArguments.HasDynamicArgument && - resolution.OverloadResolutionResult.HasAnyApplicableMember) + if (resolution.OverloadResolutionResult.HasAnyApplicableMember) { // Note that the runtime binder may consider candidates that haven't passed compile-time final validation // and an ambiguity error may be reported. Also additional checks are performed in runtime final validation @@ -1132,27 +1128,12 @@ static bool bindMethodGroupInvocation( { Debug.Assert(finalApplicableCandidates.Length > 0); - if (resolution.IsExtensionMethodGroup) - { - // error CS1973: 'T' has no applicable method named 'M' but appears to have an - // extension method by that name. Extension methods cannot be dynamically dispatched. Consider - // casting the dynamic arguments or calling the extension method without the extension method - // syntax. - - // We found an extension method, so the instance associated with the method group must have - // existed and had a type. - Debug.Assert(methodGroup.InstanceOpt?.Type is not null); - - Error(diagnostics, ErrorCode.ERR_BadArgTypeDynamicExtension, syntax, methodGroup.InstanceOpt.Type, methodGroup.Name); - addMethods = []; - result = false; - } - else - { - addMethodBinder.ReportDynamicInvocationWarnings(syntax, methodGroup, diagnostics, resolution, finalApplicableCandidates); + addMethods = filterOutBadGenericMethods(addMethodBinder, syntax, methodGroup, analyzedArguments, resolution, finalApplicableCandidates, ref useSiteInfo); + result = !addMethods.IsEmpty; - addMethods = finalApplicableCandidates.SelectAsArray(r => r.Member); - result = bindDynamicInvocation(addMethodBinder, syntax, resolution.AnalyzedArguments, diagnostics); + if (!result) + { + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, syntax, methodGroup.ReceiverOpt.Type); } } } @@ -1175,6 +1156,134 @@ static bool bindMethodGroupInvocation( return result; } + static ImmutableArray filterOutBadGenericMethods( + Binder addMethodBinder, SyntaxNode syntax, BoundMethodGroup methodGroup, AnalyzedArguments analyzedArguments, MethodGroupResolution resolution, + ImmutableArray> finalApplicableCandidates, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(methodGroup.ReceiverOpt is not null); + var resultBuilder = ArrayBuilder.GetInstance(finalApplicableCandidates.Length); + + foreach (var candidate in finalApplicableCandidates) + { + // If the method is generic, skip it if the type arguments cannot be inferred. + var member = candidate.Member; + var typeParameters = member.TypeParameters; + + if (!typeParameters.IsEmpty) + { + if (resolution.IsExtensionMethodGroup) + { + // We need to validate an ability to infer type arguments as well as check conversion to 'this' parameter. + // Overload resolution doesn't check the conversion when 'this' type refers to a type parameter + TypeSymbol? receiverType = methodGroup.ReceiverOpt.Type; + Debug.Assert(receiverType is not null); + bool thisTypeIsOpen = typeParameters.Any((typeParameter, parameter) => parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[0]); + MethodSymbol? constructed = null; + bool wasFullyInferred = false; + + if (thisTypeIsOpen) + { + constructed = ReducedExtensionMethodSymbol.InferExtensionMethodTypeArguments( + member, receiverType, addMethodBinder.Compilation, ref useSiteInfo, out wasFullyInferred); + } + + if (constructed is null || !wasFullyInferred) + { + // It is quite possible that inference failed because we didn't supply type from the second argument + if (!typeParameters.Any((typeParameter, parameter) => parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[1])) + { + continue; + } + + // Let's attempt inference with type for the second parameter + // We are going to use the second parameter's type for that + OverloadResolution.GetEffectiveParameterTypes( + member, + argumentCount: 2, + argToParamMap: default, + argumentRefKinds: analyzedArguments.RefKinds, + isMethodGroupConversion: false, + allowRefOmittedArguments: methodGroup.ReceiverOpt.IsExpressionOfComImportType(), + binder: addMethodBinder, + expanded: candidate.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm, + parameterTypes: out ImmutableArray parameterTypes, + parameterRefKinds: out ImmutableArray parameterRefKinds); + + // If we were able to infer something just from the first parameter, + // use partially substituted second type, otherwise inference might fail + // for type parameters "shared" between the parameters. + TypeSymbol secondArgumentType = (constructed ?? member).Parameters[1].Type; + + MethodTypeInferenceResult inferenceResult = MethodTypeInferrer.Infer( + addMethodBinder, + addMethodBinder.Conversions, + member.TypeParameters, + member.ContainingType, + parameterTypes, + parameterRefKinds, + ImmutableArray.Create(methodGroup.ReceiverOpt, new BoundValuePlaceholder(syntax, secondArgumentType) { WasCompilerGenerated = true }), + ref useSiteInfo); + + if (!inferenceResult.Success) + { + continue; + } + + if (thisTypeIsOpen) + { + constructed = member.Construct(inferenceResult.InferredTypeArguments); + } + } + + if (thisTypeIsOpen) + { + Debug.Assert(constructed is not null); + var conversions = constructed.ContainingAssembly.CorLibrary.TypeConversions; + var conversion = conversions.ConvertExtensionMethodThisArg(constructed.Parameters[0].Type, receiverType, ref useSiteInfo); + if (!conversion.Exists) + { + continue; // Conversion to 'this' parameter failed + } + } + } + else if (typeParameters.Any((typeParameter, parameter) => !parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[0])) + { + // A type parameter does not appear in the parameter type. + continue; + } + } + + resultBuilder.Add(member); + } + + return resultBuilder.ToImmutableAndFree(); + } + + // This is what CanEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure + static bool canEarlyBindSingleCandidateInvocationWithDynamicArgument( + Binder addMethodBinder, + SyntaxNode syntax, + BoundMethodGroup boundMethodGroup, + BindingDiagnosticBag diagnostics, + MethodGroupResolution resolution, + MemberResolutionResult methodResolutionResult, + MethodSymbol singleCandidate) + { + Debug.Assert(boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty); + + if (singleCandidate.IsGenericMethod) + { + return false; + } + + if (addMethodBinder.IsAmbiguousDynamicParamsArgument(resolution.AnalyzedArguments.Arguments, methodResolutionResult, out SyntaxNode argumentSyntax)) + { + return false; + } + + return true; + } + // This is what TryEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure static bool? tryEarlyBindSingleCandidateInvocationWithDynamicArgument( Binder addMethodBinder, @@ -1187,7 +1296,7 @@ static bool bindMethodGroupInvocation( out MethodSymbol? addMethod) { MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember; - if (!addMethodBinder.CanEarlyBindSingleCandidateInvocationWithDynamicArgument(syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) + if (!canEarlyBindSingleCandidateInvocationWithDynamicArgument(addMethodBinder, syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) { addMethod = null; return null; @@ -1290,17 +1399,40 @@ static bool bindInvocationExpressionContinued( /// return the argument to the collection initializer Add method or null if the element is not a /// collection initializer node. Otherwise, return the element as is. /// - internal static BoundExpression? GetUnderlyingCollectionExpressionElement(BoundCollectionExpression expr, BoundExpression? element) + internal static BoundExpression GetUnderlyingCollectionExpressionElement(BoundCollectionExpression expr, BoundExpression element, bool throwOnErrors) { if (expr.CollectionTypeKind is CollectionExpressionTypeKind.ImplementsIEnumerable) { - return element switch + switch (element) { - BoundCollectionElementInitializer collectionInitializer => getCollectionInitializerElement(collectionInitializer), - BoundDynamicCollectionElementInitializer dynamicInitializer => dynamicInitializer.Arguments[0], - _ => null, - }; + case BoundCollectionElementInitializer collectionInitializer: + return getCollectionInitializerElement(collectionInitializer); + case BoundDynamicCollectionElementInitializer dynamicInitializer: + return dynamicInitializer.Arguments[0]; + } + + if (throwOnErrors) + { + throw ExceptionUtilities.UnexpectedValue(element); + } + + // Handle error cases from bindCollectionInitializerElementAddMethod. + switch (element) + { + case BoundCall call: + // Overload resolution failed with one or more applicable or ambiguous + // Add methods. This case can be hit for spreads and non-spread elements. + Debug.Assert(call.HasErrors); + Debug.Assert(call.Method.Name == "Add"); + return call.Arguments[call.InvokedAsExtensionMethod ? 1 : 0]; + case BoundBadExpression badExpression: + Debug.Assert(false); // Add test if we hit this assert. + return badExpression; + default: + throw ExceptionUtilities.UnexpectedValue(element); + } } + return element; static BoundExpression getCollectionInitializerElement(BoundCollectionElementInitializer collectionInitializer) @@ -1422,7 +1554,7 @@ private void GenerateImplicitConversionErrorForCollectionExpression( } if (elements.Length > 0 && - !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, elementType, addMethods: out _, diagnostics)) + !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, addMethods: out _, diagnostics)) { reportedErrors = true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c577341e48531..ec89bd55c711e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3321,14 +3321,19 @@ private BoundExpression BindArgumentExpression(BindingDiagnosticBag diagnostics, #nullable enable private void CheckAndCoerceArguments( + SyntaxNode node, MemberResolutionResult methodResult, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics, BoundExpression? receiver, - bool invokedAsExtensionMethod) + bool invokedAsExtensionMethod, + out ImmutableArray argsToParamsOpt) where TMember : Symbol { var result = methodResult.Result; + bool expanded = result.Kind == MemberResolutionKind.ApplicableInExpandedForm; + int firstParamsArgument = -1; + ArrayBuilder? paramsArgsBuilder = null; var arguments = analyzedArguments.Arguments; // Parameter types should be taken from the least overridden member: @@ -3336,10 +3341,17 @@ private void CheckAndCoerceArguments( for (int arg = 0; arg < arguments.Count; ++arg) { - var kind = result.ConversionForArg(arg); BoundExpression argument = arguments[arg]; - if (argument is not BoundArgListOperator && !argument.HasAnyErrors) + if (argument is BoundArgListOperator) + { + Debug.Assert(result.ConversionForArg(arg).IsIdentity); + Debug.Assert(!argument.NeedsToBeConverted()); + Debug.Assert(!expanded || result.ParameterFromArgument(arg) != parameters.Length - 1); + continue; + } + + if (!argument.HasAnyErrors) { var argRefKind = analyzedArguments.RefKind(arg); @@ -3348,7 +3360,7 @@ private void CheckAndCoerceArguments( // Disallow using `ref readonly` parameters with no or `in` argument modifier, // same as older versions of the compiler would (since they would see the parameter as `ref`). if (argRefKind is RefKind.None or RefKind.In && - GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) + getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) { var available = CheckFeatureAvailability(argument.Syntax, MessageID.IDS_FeatureRefReadonlyParameters, diagnostics); Debug.Assert(!available); @@ -3361,7 +3373,7 @@ private void CheckAndCoerceArguments( // Warn for `ref`/`in` or None/`ref readonly` mismatch. if (argRefKind == RefKind.Ref) { - if (GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.In) + if (getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.In) { Debug.Assert(argNumber > 0); // The 'ref' modifier for argument {0} corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. @@ -3372,7 +3384,7 @@ private void CheckAndCoerceArguments( } } else if (argRefKind == RefKind.None && - GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) + getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) { if (!this.CheckValueKind(argument.Syntax, argument, BindValueKind.RefersToLocation, checkingReceiver: false, BindingDiagnosticBag.Discarded)) { @@ -3407,51 +3419,196 @@ private void CheckAndCoerceArguments( } } + int paramNum = result.ParameterFromArgument(arg); + + if (expanded && paramNum == parameters.Length - 1) + { + Debug.Assert(paramsArgsBuilder is null); + firstParamsArgument = arg; + paramsArgsBuilder = collectParamsArgs(in methodResult, parameters, arguments, ref arg, diagnostics); + continue; + } + + arguments[arg] = coerceArgument(in methodResult, receiver, parameters, argumentsForInterpolationConversion: arguments, argument, arg, parameters[paramNum].TypeWithAnnotations, diagnostics); + } + + argsToParamsOpt = result.ArgsToParamsOpt; + + if (paramsArgsBuilder is not null) + { + // Note, this call is going to free paramsArgsBuilder + createParamsCollection(node, in methodResult, receiver, parameters, analyzedArguments, firstParamsArgument, paramsArgsBuilder, ref argsToParamsOpt, diagnostics); + } + + Debug.Assert(analyzedArguments.RefKinds.Count == 0 || analyzedArguments.RefKinds.Count == arguments.Count); + Debug.Assert(analyzedArguments.Names.Count == 0 || analyzedArguments.Names.Count == arguments.Count); + Debug.Assert(argsToParamsOpt.IsDefault || argsToParamsOpt.Length == arguments.Count); + + result.ArgumentsWereCoerced(); + return; + + BoundExpression coerceArgument( + in MemberResolutionResult methodResult, + BoundExpression? receiver, + ImmutableArray parameters, + ArrayBuilder? argumentsForInterpolationConversion, + BoundExpression argument, + int arg, + TypeWithAnnotations parameterTypeWithAnnotations, + BindingDiagnosticBag diagnostics) + { + var result = methodResult.Result; + var kind = result.ConversionForArg(arg); + BoundExpression coercedArgument = argument; + if (kind.IsInterpolatedStringHandler) { Debug.Assert(argument is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); - arguments[arg] = BindInterpolatedStringHandlerInMemberCall(argument, parameterTypeWithAnnotations.Type, arguments, parameters, ref result, arg, receiver, diagnostics); + coercedArgument = bindInterpolatedStringHandlerInMemberCall(argument, parameterTypeWithAnnotations.Type, argumentsForInterpolationConversion, parameters, in result, arg, receiver, diagnostics); } // https://github.com/dotnet/roslyn/issues/37119 : should we create an (Identity) conversion when the kind is Identity but the types differ? else if (!kind.IsIdentity) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); - arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); + coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); } else if (argument.Kind == BoundKind.OutVariablePendingInference) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); - arguments[arg] = ((OutVariablePendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, diagnostics); + coercedArgument = ((OutVariablePendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, diagnostics); } else if (argument.Kind == BoundKind.OutDeconstructVarPendingInference) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); - arguments[arg] = ((OutDeconstructVarPendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, success: true); + coercedArgument = ((OutDeconstructVarPendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, success: true); } else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType()) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); Debug.Assert(parameterTypeWithAnnotations.HasType); - arguments[arg] = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations); + coercedArgument = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations); } else if (argument.NeedsToBeConverted()) { Debug.Assert(kind.IsIdentity); if (argument is BoundTupleLiteral) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); // CreateConversion reports tuple literal name mismatches, and constructs the expected pattern of bound nodes. - arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); + coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); } else { - arguments[arg] = BindToNaturalType(argument, diagnostics); + coercedArgument = BindToNaturalType(argument, diagnostics); } } + + return coercedArgument; + } + + static ArrayBuilder collectParamsArgs( + in MemberResolutionResult methodResult, + ImmutableArray parameters, + ArrayBuilder arguments, + ref int arg, + BindingDiagnosticBag diagnostics) + { + var result = methodResult.Result; + var paramsArgsBuilder = ArrayBuilder.GetInstance(); + int paramsIndex = parameters.Length - 1; + + while (true) + { + Debug.Assert(arguments[arg].Kind is not + (BoundKind.OutVariablePendingInference or BoundKind.OutDeconstructVarPendingInference or BoundKind.DiscardExpression or BoundKind.ArgListOperator)); + + // Conversions to elements of collection are applied in the process of collection construction + paramsArgsBuilder.Add(arguments[arg]); + + if (arg + 1 == arguments.Count || result.ParameterFromArgument(arg + 1) != paramsIndex) + { + break; + } + + arg++; + } + + return paramsArgsBuilder; + } + + // Note, this function is going to free paramsArgsBuilder + void createParamsCollection( + SyntaxNode node, + in MemberResolutionResult methodResult, + BoundExpression? receiver, + ImmutableArray parameters, + AnalyzedArguments analyzedArguments, + int firstParamsArgument, + ArrayBuilder paramsArgsBuilder, + ref ImmutableArray argsToParamsOpt, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(methodResult.Result.ParamsElementTypeOpt.HasType); + Debug.Assert(methodResult.Result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); + + int paramsIndex = parameters.Length - 1; + + if (parameters[paramsIndex].Type.IsSZArray()) + { + var result = methodResult.Result; + TypeWithAnnotations paramsElementTypeOpt = result.ParamsElementTypeOpt; + + for (int i = 0; i < paramsArgsBuilder.Count; i++) + { + paramsArgsBuilder[i] = coerceArgument( + in methodResult, receiver, parameters, + argumentsForInterpolationConversion: null, // We do not use arguments for interpolations as param array elements + paramsArgsBuilder[i], + arg: firstParamsArgument + i, + paramsElementTypeOpt, + diagnostics); + } + } + + ImmutableArray collectionArgs = paramsArgsBuilder.ToImmutableAndFree(); + Debug.Assert(collectionArgs.Length != 0); + + BoundExpression collection = CreateParamsCollection(node, parameters[paramsIndex], collectionArgs, diagnostics); + var arguments = analyzedArguments.Arguments; + + Debug.Assert(firstParamsArgument != -1); + Debug.Assert(collectionArgs.Length == 1 || firstParamsArgument + collectionArgs.Length == arguments.Count); + + ArrayBuilder? argsToParamsBuilder = null; + if (!argsToParamsOpt.IsDefault && collectionArgs.Length > 1) + { + argsToParamsBuilder = ArrayBuilder.GetInstance(argsToParamsOpt.Length); + argsToParamsBuilder.AddRange(argsToParamsOpt); + } + + for (var i = firstParamsArgument + collectionArgs.Length - 1; i != firstParamsArgument; i--) + { + arguments.RemoveAt(i); + + Debug.Assert(argsToParamsBuilder is not null || argsToParamsOpt.IsDefault); + argsToParamsBuilder?.RemoveAt(i); + + if (analyzedArguments.RefKinds is { Count: > 0 } refKindsBuilder) + { + refKindsBuilder.RemoveAt(i); + } + + if (analyzedArguments.Names is { Count: > 0 } namesBuilder) + { + namesBuilder.RemoveAt(i); + } + } + + arguments[firstParamsArgument] = collection; + + if (argsToParamsBuilder is object) + { + argsToParamsOpt = argsToParamsBuilder.ToImmutableOrNull(); + argsToParamsBuilder.Free(); + } } void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingDiagnosticBag diagnostics, BoundExpression argument, TypeWithAnnotations parameterTypeWithAnnotations) @@ -3464,28 +3621,238 @@ void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingD //CONSIDER: Return a bad expression so that HasErrors is true? } } - } - - private static ParameterSymbol GetCorrespondingParameter(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) - { - int paramNum = result.ParameterFromArgument(arg); - return parameters[paramNum]; - } -#nullable disable - - private static TypeWithAnnotations GetCorrespondingParameterTypeWithAnnotations(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) - { - int paramNum = result.ParameterFromArgument(arg); - if (paramNum == parameters.Length - 1 && result.Kind == MemberResolutionKind.ApplicableInExpandedForm) + static ParameterSymbol getCorrespondingParameter(in MemberAnalysisResult result, ImmutableArray parameters, int arg) { - Debug.Assert(result.ParamsElementTypeOpt.HasType); - Debug.Assert(result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); - return result.ParamsElementTypeOpt; + int paramNum = result.ParameterFromArgument(arg); + return parameters[paramNum]; } - return parameters[paramNum].TypeWithAnnotations; + BoundExpression bindInterpolatedStringHandlerInMemberCall( + BoundExpression unconvertedString, + TypeSymbol handlerType, + ArrayBuilder? arguments, + ImmutableArray parameters, + in MemberAnalysisResult memberAnalysisResult, + int interpolatedStringArgNum, + BoundExpression? receiver, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(unconvertedString is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); + var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); + Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); + Debug.Assert(handlerType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); + + var correspondingParameter = getCorrespondingParameter(in memberAnalysisResult, parameters, interpolatedStringArgNum); + var handlerParameterIndexes = correspondingParameter.InterpolatedStringHandlerArgumentIndexes; + + if (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && correspondingParameter.Ordinal == parameters.Length - 1) + { + Debug.Assert(handlerParameterIndexes.IsEmpty); + + // No arguments, fall back to the standard conversion steps. + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + handlerType, + diagnostics); + } + + Debug.Assert(arguments is not null); + + if (correspondingParameter.HasInterpolatedStringHandlerArgumentError) + { + // The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, unconvertedString.Syntax.Location, correspondingParameter, handlerType); + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + wasCompilerGenerated: false, + handlerType, + diagnostics, + hasErrors: true); + } + + if (handlerParameterIndexes.IsEmpty) + { + // No arguments, fall back to the standard conversion steps. + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + handlerType, + diagnostics); + } + + Debug.Assert(handlerParameterIndexes.All((index, paramLength) => index >= BoundInterpolatedStringArgumentPlaceholder.InstanceParameter && index < paramLength, + parameters.Length)); + + // We need to find the appropriate argument expression for every expected parameter, and error on any that occur after the current parameter + + ImmutableArray handlerArgumentIndexes; + + if (memberAnalysisResult.ArgsToParamsOpt.IsDefault && arguments.Count == parameters.Length) + { + // No parameters are missing and no remapped indexes, we can just use the original indexes + handlerArgumentIndexes = handlerParameterIndexes; + } + else + { + // Args and parameters were reordered via named parameters, or parameters are missing. Find the correct argument index for each parameter. + var handlerArgumentIndexesBuilder = ArrayBuilder.GetInstance(handlerParameterIndexes.Length, fillWithValue: BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + for (int handlerParameterIndex = 0; handlerParameterIndex < handlerParameterIndexes.Length; handlerParameterIndex++) + { + int handlerParameter = handlerParameterIndexes[handlerParameterIndex]; + Debug.Assert(handlerArgumentIndexesBuilder[handlerParameterIndex] is BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + + if (handlerParameter == BoundInterpolatedStringArgumentPlaceholder.InstanceParameter) + { + handlerArgumentIndexesBuilder[handlerParameterIndex] = handlerParameter; + continue; + } + + for (int argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) + { + // The index in the original parameter list we're looking to match up. + int argumentParameterIndex = memberAnalysisResult.ParameterFromArgument(argumentIndex); + // Is the original parameter index of the current argument the parameter index that was specified in the attribute? + if (argumentParameterIndex == handlerParameter) + { + // We can't just bail out on the first match: users can duplicate parameters in attributes, causing the same value to be passed twice. + handlerArgumentIndexesBuilder[handlerParameterIndex] = argumentIndex; + } + } + } + + handlerArgumentIndexes = handlerArgumentIndexesBuilder.ToImmutableAndFree(); + } + + var argumentPlaceholdersBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + var argumentRefKindsBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + bool hasErrors = false; + + // Now, go through all the specified arguments and see if any were specified _after_ the interpolated string, and construct + // a set of placeholders for overload resolution. + for (int i = 0; i < handlerArgumentIndexes.Length; i++) + { + int argumentIndex = handlerArgumentIndexes[i]; + Debug.Assert(argumentIndex != interpolatedStringArgNum); + + RefKind refKind; + TypeSymbol placeholderType; + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(receiver!.Type is not null); + refKind = RefKind.None; + placeholderType = receiver.Type; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + { + // Don't error if the parameter isn't optional or params: the user will already have an error for missing an optional parameter or overload resolution failed. + // If it is optional, then they could otherwise not specify the parameter and that's an error + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (parameter.IsOptional || + (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && originalParameterIndex + 1 == parameters.Length)) + { + // Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, + unconvertedString.Syntax.Location, + parameter.Name, + correspondingParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + default: + { + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (argumentIndex > interpolatedStringArgNum) + { + // Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, + arguments[argumentIndex].Syntax.Location, + parameter.Name, + correspondingParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + } + + SyntaxNode placeholderSyntax; + bool isSuppressed; + + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(receiver != null); + isSuppressed = receiver.IsSuppressed; + placeholderSyntax = receiver.Syntax; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + placeholderSyntax = unconvertedString.Syntax; + isSuppressed = false; + break; + case >= 0: + placeholderSyntax = arguments[argumentIndex].Syntax; + isSuppressed = arguments[argumentIndex].IsSuppressed; + break; + default: + throw ExceptionUtilities.UnexpectedValue(argumentIndex); + } + + argumentPlaceholdersBuilder.Add( + (BoundInterpolatedStringArgumentPlaceholder)(new BoundInterpolatedStringArgumentPlaceholder( + placeholderSyntax, + argumentIndex, + placeholderType, + hasErrors: argumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter) + { WasCompilerGenerated = true }.WithSuppression(isSuppressed))); + // We use the parameter refkind, rather than what the argument was actually passed with, because that will suppress duplicated errors + // about arguments being passed with the wrong RefKind. The user will have already gotten an error about mismatched RefKinds or it will + // be a place where refkinds are allowed to differ + argumentRefKindsBuilder.Add(refKind == RefKind.RefReadOnlyParameter ? RefKind.In : refKind); + } + + var interpolatedString = BindUnconvertedInterpolatedExpressionToHandlerType( + unconvertedString, + (NamedTypeSymbol)handlerType, + diagnostics, + additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), + additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); + + return new BoundConversion( + interpolatedString.Syntax, + interpolatedString, + interpolatedStringConversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + constantValueOpt: null, + handlerType, + hasErrors || interpolatedString.HasErrors); + } } +#nullable disable private BoundExpression BindArrayCreationExpression(ArrayCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { @@ -4536,9 +4903,15 @@ private BoundExpression BindConstructorInitializerCoreContinued( { ReportConstructorUseSiteDiagnostics(errorLocation, diagnostics, suppressUnsupportedRequiredMembersError: true, in overloadResolutionUseSiteInfo); + ImmutableArray argsToParamsOpt; + if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(nonNullSyntax, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argsToParamsOpt); + } + else + { + argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; } NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics; @@ -4577,7 +4950,6 @@ private BoundExpression BindConstructorInitializerCoreContinued( ReportDiagnosticsIfObsolete(diagnostics, resultMember, nonNullSyntax, hasBaseReceiver: isBaseConstructorInitializer); var expanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; if (constructor is SynthesizedPrimaryConstructor primaryConstructor) { @@ -4615,7 +4987,7 @@ private BoundExpression BindConstructorInitializerCoreContinued( primaryConstructor.SetParametersPassedToTheBase(parametersPassedToBase); } - BindDefaultArgumentsAndParamsCollection(nonNullSyntax, resultMember.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParamsOpt, out var defaultArguments, expanded, enableCallerInfo, diagnostics); + BindDefaultArguments(nonNullSyntax, resultMember.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParamsOpt, out var defaultArguments, expanded, enableCallerInfo, diagnostics); var arguments = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); @@ -5933,7 +6305,7 @@ boundElementInitializerExpressions[0] is not var d = BindingDiagnosticBag.GetInstance(); // This assert provides some validation that, if the real invocation binding succeeds, then the HasCollectionExpressionApplicableAddMethod helper succeeds as well. - Debug.Assert(collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, boundElementInitializerExpressions[0].Type, addMethods: out _, d)); + Debug.Assert(collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, addMethods: out _, d)); d.Free(); } @@ -6303,9 +6675,15 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( ReportConstructorUseSiteDiagnostics(typeNode.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); + ImmutableArray argToParams; + if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argToParams); + } + else + { + argToParams = memberResolutionResult.Result.ArgsToParamsOpt; } var method = memberResolutionResult.Member; @@ -6331,8 +6709,7 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( null; var expanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argToParams = memberResolutionResult.Result.ArgsToParamsOpt; - BindDefaultArgumentsAndParamsCollection(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); var arguments = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); @@ -6383,7 +6760,7 @@ private BoundExpression CreateBadClassCreationExpression( if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, argsToParamsOpt: out _); } LookupResultKind resultKind; @@ -7919,7 +8296,8 @@ protected MethodGroupResolution BindExtensionMethod( OverloadResolution.Options.IsFunctionPointerResolution | OverloadResolution.Options.InferWithDynamic | OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter | - OverloadResolution.Options.DynamicResolution)) == 0); + OverloadResolution.Options.DynamicResolution | + OverloadResolution.Options.DynamicConvertsToAnything)) == 0); var firstResult = new MethodGroupResolution(); AnalyzedArguments actualArguments = null; @@ -9433,9 +9811,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; - var isExpanded = resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParams = resolutionResult.Result.ArgsToParamsOpt; - ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: receiver != null && receiver.Kind == BoundKind.BaseReference); // Make sure that the result of overload resolution is valid. @@ -9443,7 +9818,8 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( receiver = ReplaceTypeOrValueReceiver(receiver, property.IsStatic, diagnostics); - this.CheckAndCoerceArguments(resolutionResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: false); + ImmutableArray argsToParams; + this.CheckAndCoerceArguments(syntax, resolutionResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: false, out argsToParams); if (!gotError && receiver != null && receiver.Kind == BoundKind.ThisReference && receiver.WasCompilerGenerated) { @@ -9462,7 +9838,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( arguments, argumentNames, argumentRefKinds, - isExpanded, + expanded: resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm, argsToParams, defaultArguments: default, property.Type, @@ -9980,7 +10356,8 @@ private MethodGroupResolution ResolveDefaultMethodGroup( OverloadResolution.Options.IsFunctionPointerResolution | OverloadResolution.Options.InferWithDynamic | OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter | - OverloadResolution.Options.DynamicResolution)) == 0); + OverloadResolution.Options.DynamicResolution | + OverloadResolution.Options.DynamicConvertsToAnything)) == 0); var methods = node.Methods; if (methods.Length == 0) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index c5a27ffff11b7..0a15fecb093c9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs @@ -873,227 +873,5 @@ private ImmutableArray BindInterpolatedStringParts(BoundUnconve positionInfo.Free(); return (builderAppendCallsArray.ToImmutableAndFree(), builderPatternExpectsBool ?? false, positionInfoArray.ToImmutableAndFree(), baseStringLength, numFormatHoles); } - - private BoundExpression BindInterpolatedStringHandlerInMemberCall( - BoundExpression unconvertedString, - TypeSymbol handlerType, - ArrayBuilder arguments, - ImmutableArray parameters, - ref MemberAnalysisResult memberAnalysisResult, - int interpolatedStringArgNum, - BoundExpression? receiver, - BindingDiagnosticBag diagnostics) - { - Debug.Assert(unconvertedString is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); - var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); - Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); - Debug.Assert(handlerType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); - - var correspondingParameter = GetCorrespondingParameter(ref memberAnalysisResult, parameters, interpolatedStringArgNum); - var handlerParameterIndexes = correspondingParameter.InterpolatedStringHandlerArgumentIndexes; - - if (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && correspondingParameter.Ordinal == parameters.Length - 1) - { - Debug.Assert(handlerParameterIndexes.IsEmpty); - - // No arguments, fall back to the standard conversion steps. - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - handlerType, - diagnostics); - } - - if (correspondingParameter.HasInterpolatedStringHandlerArgumentError) - { - // The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. - diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, unconvertedString.Syntax.Location, correspondingParameter, handlerType); - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - wasCompilerGenerated: false, - handlerType, - diagnostics, - hasErrors: true); - } - - if (handlerParameterIndexes.IsEmpty) - { - // No arguments, fall back to the standard conversion steps. - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - handlerType, - diagnostics); - } - - Debug.Assert(handlerParameterIndexes.All((index, paramLength) => index >= BoundInterpolatedStringArgumentPlaceholder.InstanceParameter && index < paramLength, - parameters.Length)); - - // We need to find the appropriate argument expression for every expected parameter, and error on any that occur after the current parameter - - ImmutableArray handlerArgumentIndexes; - - if (memberAnalysisResult.ArgsToParamsOpt.IsDefault && arguments.Count == parameters.Length) - { - // No parameters are missing and no remapped indexes, we can just use the original indexes - handlerArgumentIndexes = handlerParameterIndexes; - } - else - { - // Args and parameters were reordered via named parameters, or parameters are missing. Find the correct argument index for each parameter. - var handlerArgumentIndexesBuilder = ArrayBuilder.GetInstance(handlerParameterIndexes.Length, fillWithValue: BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); - for (int handlerParameterIndex = 0; handlerParameterIndex < handlerParameterIndexes.Length; handlerParameterIndex++) - { - int handlerParameter = handlerParameterIndexes[handlerParameterIndex]; - Debug.Assert(handlerArgumentIndexesBuilder[handlerParameterIndex] is BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); - - if (handlerParameter == BoundInterpolatedStringArgumentPlaceholder.InstanceParameter) - { - handlerArgumentIndexesBuilder[handlerParameterIndex] = handlerParameter; - continue; - } - - for (int argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) - { - // The index in the original parameter list we're looking to match up. - int argumentParameterIndex = memberAnalysisResult.ParameterFromArgument(argumentIndex); - // Is the original parameter index of the current argument the parameter index that was specified in the attribute? - if (argumentParameterIndex == handlerParameter) - { - // We can't just bail out on the first match: users can duplicate parameters in attributes, causing the same value to be passed twice. - handlerArgumentIndexesBuilder[handlerParameterIndex] = argumentIndex; - } - } - } - - handlerArgumentIndexes = handlerArgumentIndexesBuilder.ToImmutableAndFree(); - } - - var argumentPlaceholdersBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); - var argumentRefKindsBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); - bool hasErrors = false; - - // Now, go through all the specified arguments and see if any were specified _after_ the interpolated string, and construct - // a set of placeholders for overload resolution. - for (int i = 0; i < handlerArgumentIndexes.Length; i++) - { - int argumentIndex = handlerArgumentIndexes[i]; - Debug.Assert(argumentIndex != interpolatedStringArgNum); - - RefKind refKind; - TypeSymbol placeholderType; - switch (argumentIndex) - { - case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: - Debug.Assert(receiver!.Type is not null); - refKind = RefKind.None; - placeholderType = receiver.Type; - break; - case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: - { - // Don't error if the parameter isn't optional or params: the user will already have an error for missing an optional parameter or overload resolution failed. - // If it is optional, then they could otherwise not specify the parameter and that's an error - var originalParameterIndex = handlerParameterIndexes[i]; - var parameter = parameters[originalParameterIndex]; - if (parameter.IsOptional || - (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && originalParameterIndex + 1 == parameters.Length)) - { - // Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. - diagnostics.Add( - ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, - unconvertedString.Syntax.Location, - parameter.Name, - correspondingParameter.Name); - hasErrors = true; - } - - refKind = parameter.RefKind; - placeholderType = parameter.Type; - } - break; - default: - { - var originalParameterIndex = handlerParameterIndexes[i]; - var parameter = parameters[originalParameterIndex]; - if (argumentIndex > interpolatedStringArgNum) - { - // Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. - diagnostics.Add( - ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, - arguments[argumentIndex].Syntax.Location, - parameter.Name, - correspondingParameter.Name); - hasErrors = true; - } - - refKind = parameter.RefKind; - placeholderType = parameter.Type; - } - break; - } - - SyntaxNode placeholderSyntax; - bool isSuppressed; - - switch (argumentIndex) - { - case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: - Debug.Assert(receiver != null); - isSuppressed = receiver.IsSuppressed; - placeholderSyntax = receiver.Syntax; - break; - case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: - placeholderSyntax = unconvertedString.Syntax; - isSuppressed = false; - break; - case >= 0: - placeholderSyntax = arguments[argumentIndex].Syntax; - isSuppressed = arguments[argumentIndex].IsSuppressed; - break; - default: - throw ExceptionUtilities.UnexpectedValue(argumentIndex); - } - - argumentPlaceholdersBuilder.Add( - (BoundInterpolatedStringArgumentPlaceholder)(new BoundInterpolatedStringArgumentPlaceholder( - placeholderSyntax, - argumentIndex, - placeholderType, - hasErrors: argumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter) - { WasCompilerGenerated = true }.WithSuppression(isSuppressed))); - // We use the parameter refkind, rather than what the argument was actually passed with, because that will suppress duplicated errors - // about arguments being passed with the wrong RefKind. The user will have already gotten an error about mismatched RefKinds or it will - // be a place where refkinds are allowed to differ - argumentRefKindsBuilder.Add(refKind == RefKind.RefReadOnlyParameter ? RefKind.In : refKind); - } - - var interpolatedString = BindUnconvertedInterpolatedExpressionToHandlerType( - unconvertedString, - (NamedTypeSymbol)handlerType, - diagnostics, - additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), - additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); - - return new BoundConversion( - interpolatedString.Syntax, - interpolatedString, - interpolatedStringConversion, - @checked: CheckOverflowAtRuntime, - explicitCastInCode: false, - conversionGroupOpt: null, - constantValueOpt: null, - handlerType, - hasErrors || interpolatedString.HasErrors); - } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 366a0e05659ac..3b7c36fb9997c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -388,14 +388,6 @@ private BoundExpression BindDynamicInvocation( BindingDiagnosticBag diagnostics, CSharpSyntaxNode queryClause) { - // - // !!! ATTENTION !!! - // - // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check - // this function should be kept in sync with local function - // HasCollectionExpressionApplicableAddMethod.bindDynamicInvocation - // - CheckNamedArgumentsForDynamicInvocation(arguments, diagnostics); bool hasErrors = false; @@ -891,6 +883,14 @@ private bool CanEarlyBindSingleCandidateInvocationWithDynamicArgument( MemberResolutionResult methodResolutionResult, MethodSymbol singleCandidate) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.canEarlyBindSingleCandidateInvocationWithDynamicArgument + // + if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && singleCandidate.IsGenericMethod) { // If we call an unconstructed generic function with a @@ -1221,12 +1221,12 @@ private BoundCall BindInvocationExpressionContinued( var receiver = ReplaceTypeOrValueReceiver(methodGroup.Receiver, !method.RequiresInstanceReceiver && !invokedAsExtensionMethod, diagnostics); - this.CheckAndCoerceArguments(methodResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: invokedAsExtensionMethod); + ImmutableArray argsToParams; + this.CheckAndCoerceArguments(node, methodResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: invokedAsExtensionMethod, out argsToParams); var expanded = methodResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParams = methodResult.Result.ArgsToParamsOpt; - BindDefaultArgumentsAndParamsCollection(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); // Note: we specifically want to do final validation (7.6.5.1) without checking delegate compatibility (15.2), // so we're calling MethodGroupFinalValidation directly, rather than via MethodGroupConversionHasErrors. @@ -1487,7 +1487,7 @@ private BoundExpression GetDefaultParameterSpecialNoConversion(SyntaxNode syntax return parameter; } - internal void BindDefaultArgumentsAndParamsCollection( + internal void BindDefaultArguments( SyntaxNode node, ImmutableArray parameters, ArrayBuilder argumentsBuilder, @@ -1500,12 +1500,7 @@ internal void BindDefaultArgumentsAndParamsCollection( BindingDiagnosticBag diagnostics, Symbol? attributedMember = null) { - int firstParamsArgument = -1; int paramsIndex = parameters.Length - 1; - var paramsArgsBuilder = expanded ? ArrayBuilder.GetInstance() : null; - - Debug.Assert(!argumentsBuilder.Any(a => a.IsParamsArrayOrCollection)); - var visitedParameters = BitVector.Create(parameters.Length); for (var i = 0; i < argumentsBuilder.Count; i++) { @@ -1516,22 +1511,10 @@ internal void BindDefaultArgumentsAndParamsCollection( if (expanded && parameter.Ordinal == paramsIndex) { - Debug.Assert(paramsArgsBuilder is not null); - Debug.Assert(paramsArgsBuilder.Count == 0); - - firstParamsArgument = i; - paramsArgsBuilder.Add(argumentsBuilder[i]); - - for (int remainingArgument = i + 1; remainingArgument < argumentsBuilder.Count; ++remainingArgument) - { - if (GetCorrespondingParameter(remainingArgument, parameters, argsToParamsOpt, expanded: true)?.Ordinal != paramsIndex) - { - break; - } - - paramsArgsBuilder.Add(argumentsBuilder[remainingArgument]); - i++; - } + expanded = false; // For the reminder of the method treat this as non-expanded case + Debug.Assert(argumentsBuilder[i].IsParamsArrayOrCollection); + Debug.Assert(i + 1 == argumentsBuilder.Count || + GetCorrespondingParameter(i + 1, parameters, argsToParamsOpt, expanded: true)?.Ordinal != paramsIndex); } } } @@ -1550,7 +1533,6 @@ internal void BindDefaultArgumentsAndParamsCollection( Debug.Assert(argumentRefKindsBuilder is null || argumentRefKindsBuilder.Count == 0 || argumentRefKindsBuilder.Count == argumentsBuilder.Count); Debug.Assert(namesBuilder is null || namesBuilder.Count == 0 || namesBuilder.Count == argumentsBuilder.Count); Debug.Assert(argsToParamsOpt.IsDefault || argsToParamsOpt.Length == argumentsBuilder.Count); - Debug.Assert(paramsArgsBuilder is null); defaultArguments = default; return; } @@ -1562,146 +1544,6 @@ internal void BindDefaultArgumentsAndParamsCollection( argsToParamsBuilder.AddRange(argsToParamsOpt); } - BoundExpression? collection = null; - - if (expanded) - { - Debug.Assert(paramsArgsBuilder is not null); - ImmutableArray collectionArgs = paramsArgsBuilder.ToImmutableAndFree(); - int collectionArgsLength = collectionArgs.Length; - - TypeSymbol collectionType = parameters[paramsIndex].Type; - - if (collectionType is ArrayTypeSymbol { IsSZArray: true }) - { - TypeSymbol int32Type = GetSpecialType(SpecialType.System_Int32, diagnostics, node); - BoundExpression arraySize = new BoundLiteral(node, ConstantValue.Create(collectionArgsLength), int32Type) { WasCompilerGenerated = true }; - - collection = new BoundArrayCreation( - node, - ImmutableArray.Create(arraySize), - new BoundArrayInitialization(node, isInferred: false, collectionArgs) { WasCompilerGenerated = true }, - collectionType) - { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - } - else - { - if (Compilation.SourceModule != parameters[paramsIndex].ContainingModule) - { - MessageID.IDS_FeatureParamsCollections.CheckFeatureAvailability(diagnostics, node); - } - - var unconvertedCollection = new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(collectionArgs)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - Conversion conversion = Conversions.ClassifyImplicitConversionFromExpression(unconvertedCollection, collectionType, ref useSiteInfo); - diagnostics.Add(node, useSiteInfo); - - BoundCollectionExpression converted; - if (!conversion.Exists) - { - Debug.Assert(false); // Add test if this code path is reachable - GenerateImplicitConversionErrorForCollectionExpression(unconvertedCollection, collectionType, diagnostics); - converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); - } - else - { - Debug.Assert(conversion.IsCollectionExpression); - - bool infiniteRecursion = false; - if (conversion.GetCollectionExpressionTypeKind(out _, out MethodSymbol? constructor, out bool isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && - isExpanded) - { - Debug.Assert(constructor is not null); - - // Check for infinite recursion through the constructor - var constructorSet = PooledHashSet.GetInstance(); - constructorSet.Add(constructor.OriginalDefinition); - - BoundUnconvertedCollectionExpression? emptyCollection = null; - - while (true) - { - var paramsType = constructor.Parameters[^1].Type; - if (!paramsType.IsSZArray()) - { - var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - emptyCollection ??= new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(ImmutableArray.Empty)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - Conversion nextConversion = Conversions.ClassifyImplicitConversionFromExpression(emptyCollection, paramsType, ref discardedUseSiteInfo); - - if (nextConversion.Exists && - nextConversion.GetCollectionExpressionTypeKind(out _, out constructor, out isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && - isExpanded) - { - Debug.Assert(constructor is not null); - - if (constructorSet.Add(constructor.OriginalDefinition)) - { - continue; - } - - infiniteRecursion = true; - } - } - - break; - } - - constructorSet.Free(); - } - - if (infiniteRecursion) - { - Debug.Assert(constructor is not null); - Error(diagnostics, ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, node, collectionType, constructor.OriginalDefinition); - converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); - } - else - { - converted = ConvertCollectionExpression(unconvertedCollection, collectionType, conversion, diagnostics); - } - } - - collection = new BoundConversion( - node, - converted, - conversion, - @checked: CheckOverflowAtRuntime, - explicitCastInCode: false, - conversionGroupOpt: null, - constantValueOpt: null, - type: collectionType) - { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - } - - Debug.Assert(collection.IsParamsArrayOrCollection); - - if (collectionArgsLength != 0) - { - Debug.Assert(firstParamsArgument != -1); - Debug.Assert(!haveDefaultArguments || collectionArgsLength == 1); - Debug.Assert(collectionArgsLength == 1 || firstParamsArgument + collectionArgsLength == argumentsBuilder.Count); - - for (var i = firstParamsArgument + collectionArgsLength - 1; i != firstParamsArgument; i--) - { - argumentsBuilder.RemoveAt(i); - argsToParamsBuilder?.RemoveAt(i); - - if (argumentRefKindsBuilder is { Count: > 0 }) - { - argumentRefKindsBuilder.RemoveAt(i); - } - - if (namesBuilder is { Count: > 0 }) - { - namesBuilder.RemoveAt(i); - } - } - - argumentsBuilder[firstParamsArgument] = collection; - collection = null; - } - } - // only proceed with binding default arguments if we know there is some parameter that has not been matched by an explicit argument if (haveDefaultArguments) { @@ -1750,11 +1592,10 @@ internal void BindDefaultArgumentsAndParamsCollection( defaultArguments = default; } - if (collection is not null) + if (expanded) { - Debug.Assert(expanded); - Debug.Assert(firstParamsArgument == -1); - + // Create an empty collection + BoundExpression collection = CreateParamsCollection(node, parameters[paramsIndex], collectionArgs: ImmutableArray.Empty, diagnostics); argumentsBuilder.Add(collection); argsToParamsBuilder?.Add(paramsIndex); @@ -1900,6 +1741,116 @@ static int getArgumentIndex(int parameterIndex, ImmutableArray argsToParams } + private BoundExpression CreateParamsCollection(SyntaxNode node, ParameterSymbol paramsParameter, ImmutableArray collectionArgs, BindingDiagnosticBag diagnostics) + { + TypeSymbol collectionType = paramsParameter.Type; + BoundExpression collection; + + if (collectionType is ArrayTypeSymbol { IsSZArray: true }) + { + TypeSymbol int32Type = GetSpecialType(SpecialType.System_Int32, diagnostics, node); + BoundExpression arraySize = new BoundLiteral(node, ConstantValue.Create(collectionArgs.Length), int32Type) { WasCompilerGenerated = true }; + + collection = new BoundArrayCreation( + node, + ImmutableArray.Create(arraySize), + new BoundArrayInitialization(node, isInferred: false, collectionArgs) { WasCompilerGenerated = true }, + collectionType) + { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + } + else + { + if (Compilation.SourceModule != paramsParameter.ContainingModule) + { + MessageID.IDS_FeatureParamsCollections.CheckFeatureAvailability(diagnostics, node); + } + + var unconvertedCollection = new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(collectionArgs)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + Conversion conversion = Conversions.ClassifyImplicitConversionFromExpression(unconvertedCollection, collectionType, ref useSiteInfo); + diagnostics.Add(node, useSiteInfo); + + BoundCollectionExpression converted; + if (!conversion.Exists) + { + Debug.Assert(false); // Add test if this code path is reachable + GenerateImplicitConversionErrorForCollectionExpression(unconvertedCollection, collectionType, diagnostics); + converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); + } + else + { + Debug.Assert(conversion.IsCollectionExpression); + + bool infiniteRecursion = false; + if (conversion.GetCollectionExpressionTypeKind(out _, out MethodSymbol? constructor, out bool isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && + isExpanded) + { + Debug.Assert(constructor is not null); + + // Check for infinite recursion through the constructor + var constructorSet = PooledHashSet.GetInstance(); + constructorSet.Add(constructor.OriginalDefinition); + + BoundUnconvertedCollectionExpression? emptyCollection = null; + + while (true) + { + var paramsType = constructor.Parameters[^1].Type; + if (!paramsType.IsSZArray()) + { + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + emptyCollection ??= new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(ImmutableArray.Empty)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + Conversion nextConversion = Conversions.ClassifyImplicitConversionFromExpression(emptyCollection, paramsType, ref discardedUseSiteInfo); + + if (nextConversion.Exists && + nextConversion.GetCollectionExpressionTypeKind(out _, out constructor, out isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && + isExpanded) + { + Debug.Assert(constructor is not null); + + if (constructorSet.Add(constructor.OriginalDefinition)) + { + continue; + } + + infiniteRecursion = true; + } + } + + break; + } + + constructorSet.Free(); + } + + if (infiniteRecursion) + { + Debug.Assert(constructor is not null); + Error(diagnostics, ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, node, collectionType, constructor.OriginalDefinition); + converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); + } + else + { + converted = ConvertCollectionExpression(unconvertedCollection, collectionType, conversion, diagnostics); + } + } + + collection = new BoundConversion( + node, + converted, + conversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + constantValueOpt: null, + type: collectionType) + { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + } + + Debug.Assert(collection.IsParamsArrayOrCollection); + return collection; + } + #nullable disable /// @@ -2516,7 +2467,7 @@ private BoundFunctionPointerInvocation BindFunctionPointerInvocation(SyntaxNode methodsBuilder.Free(); MemberResolutionResult methodResult = overloadResolutionResult.ValidResult; - CheckAndCoerceArguments(methodResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + CheckAndCoerceArguments(node, methodResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, argsToParamsOpt: out _); var args = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 0bc9e18eb7df9..c2db44e853310 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1205,7 +1205,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat var argsBuilder = ArrayBuilder.GetInstance(patternDisposeMethod.ParameterCount); var argsToParams = default(ImmutableArray); - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, patternDisposeMethod.Parameters, argsBuilder, @@ -1429,9 +1429,10 @@ private MethodArgumentInfo PerformForEachPatternOverloadResolution(SyntaxNode sy } else { + Debug.Assert(analyzedArguments.Arguments.Count == 0); var argsToParams = overloadResolutionResult.ValidResult.Result.ArgsToParamsOpt; var expanded = overloadResolutionResult.ValidResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, result.Parameters, analyzedArguments.Arguments, @@ -1883,7 +1884,7 @@ private MethodArgumentInfo BindDefaultArguments(MethodSymbol method, BoundExpres } ImmutableArray argsToParams = default; - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, method.Parameters, argsBuilder, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 3332390061a99..abb608ffe3b20 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -194,7 +194,7 @@ protected override Conversion GetCollectionExpressionConversion( } if (elements.Length > 0 && - !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, elementType, addMethods: out _, BindingDiagnosticBag.Discarded)) + !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, addMethods: out _, BindingDiagnosticBag.Discarded)) { return Conversion.NoConversion; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index 30580a2a6e42f..f0a6c46065c3a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -14,8 +14,79 @@ namespace Microsoft.CodeAnalysis.CSharp { [SuppressMessage("Performance", "CA1067", Justification = "Equality not actually implemented")] - internal readonly struct MemberAnalysisResult + internal +#if !DEBUG + readonly +#endif + struct MemberAnalysisResult { +#if DEBUG + private readonly ImmutableArray _conversionsOpt; + public ImmutableArray ConversionsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _conversionsOpt; + } + private init + { + _conversionsOpt = value; + } + } + + /// + /// A bit vector representing whose true bits indicate indices of bad arguments + /// + /// + /// The capacity of this BitVector might not match the parameter count of the method overload being resolved. + /// For example, if a method overload has 5 parameters and the second parameter is the only bad parameter, then this + /// BitVector could end up with Capacity being 2 where BadArguments[0] is false and BadArguments[1] is true. + /// + private readonly BitVector _badArgumentsOpt; + public BitVector BadArgumentsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _badArgumentsOpt; + } + private init + { + _badArgumentsOpt = value; + } + } + + private readonly ImmutableArray _argsToParamsOpt; + public ImmutableArray ArgsToParamsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _argsToParamsOpt; + } + private init + { + _argsToParamsOpt = value; + } + } + + private readonly ImmutableArray _constraintFailureDiagnostics; + public ImmutableArray ConstraintFailureDiagnostics + { + get + { + Debug.Assert(!_argumentsCoerced); + return _constraintFailureDiagnostics; + } + private init + { + _constraintFailureDiagnostics = value; + } + } + + private bool _argumentsCoerced; +#else // put these first for better packing public readonly ImmutableArray ConversionsOpt; @@ -30,6 +101,7 @@ internal readonly struct MemberAnalysisResult public readonly BitVector BadArgumentsOpt; public readonly ImmutableArray ArgsToParamsOpt; public readonly ImmutableArray ConstraintFailureDiagnostics; +#endif public readonly int BadParameter; public readonly MemberResolutionKind Kind; @@ -325,5 +397,13 @@ internal static MemberAnalysisResult WrongCallingConvention() { return new MemberAnalysisResult(MemberResolutionKind.WrongCallingConvention); } + + [Conditional("DEBUG")] + public void ArgumentsWereCoerced() + { +#if DEBUG + _argumentsCoerced = true; +#endif + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 4253977f7544a..d70eed323fcdc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -126,6 +126,7 @@ public enum Options : byte IsFunctionPointerResolution = 0b_00010000, IsExtensionMethodResolution = 0b_00100000, DynamicResolution = 0b_01000000, + DynamicConvertsToAnything = 0b_10000000, } // Perform overload resolution on the given method group, with the given arguments and @@ -870,6 +871,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, completeResults: completeResults, + dynamicConvertsToAnything: false, useSiteInfo: ref useSiteInfo); } @@ -911,6 +913,7 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm( hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, completeResults: completeResults, + dynamicConvertsToAnything: false, useSiteInfo: ref useSiteInfo); Debug.Assert(!result.IsValid || result.Kind == MemberResolutionKind.ApplicableInExpandedForm); @@ -1063,6 +1066,7 @@ private void AddMemberToCandidateSet( arguments, allowRefOmittedArguments: (options & Options.AllowRefOmittedArguments) != 0, completeResults: completeResults, + dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, useSiteInfo: ref useSiteInfo); if (PreferExpandedFormOverNormalForm(normalResult, expandedResult)) @@ -1210,7 +1214,7 @@ public static bool TryInferParamsCollectionIterationType(Binder binder, TypeSymb return false; } - if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, addMethods: out _, BindingDiagnosticBag.Discarded)) { return false; } @@ -3706,6 +3710,7 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, inferWithDynamic: (options & Options.InferWithDynamic) != 0, completeResults: completeResults, + dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, useSiteInfo: ref useSiteInfo); // If we were producing complete results and had missing arguments, we pushed on in order to call IsApplicable for @@ -3725,6 +3730,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm useSiteInfo) where TMember : Symbol { @@ -3767,6 +3773,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm IsApplicable( bool hasAnyRefOmittedArgument, bool inferWithDynamic, bool completeResults, + bool dynamicConvertsToAnything, ref CompoundUseSiteInfo useSiteInfo) where TMember : Symbol { @@ -3902,6 +3910,7 @@ private MemberResolutionResult IsApplicable( hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, ignoreOpenTypes: ignoreOpenTypes, completeResults: completeResults, + dynamicConvertsToAnything: dynamicConvertsToAnything, useSiteInfo: ref useSiteInfo); return new MemberResolutionResult(member, leastOverriddenMember, applicableResult, hasTypeArgumentsInferredFromFunctionType); } @@ -3971,6 +3980,7 @@ private MemberAnalysisResult IsApplicable( bool hasAnyRefOmittedArgument, bool ignoreOpenTypes, bool completeResults, + bool dynamicConvertsToAnything, ref CompoundUseSiteInfo useSiteInfo) { TypeWithAnnotations paramsElementTypeOpt; @@ -4083,9 +4093,15 @@ private MemberAnalysisResult IsApplicable( ignoreOpenTypes, ref useSiteInfo, forExtensionMethodThisArg, - hasInterpolatedStringRefMismatch); + hasInterpolatedStringRefMismatch, + dynamicConvertsToAnything); - if (forExtensionMethodThisArg && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) + Debug.Assert( + !forExtensionMethodThisArg || + (!conversion.IsDynamic || + (ignoreOpenTypes && parameters.ParameterTypes[argumentPosition].Type.ContainsTypeParameter(parameterContainer: (MethodSymbol)candidate)))); + + if (forExtensionMethodThisArg && !conversion.IsDynamic && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) { // Return early, without checking conversions of subsequent arguments, // if the instance argument is not convertible to the 'this' parameter, @@ -4154,7 +4170,8 @@ private Conversion CheckArgumentForApplicability( bool ignoreOpenTypes, ref CompoundUseSiteInfo useSiteInfo, bool forExtensionMethodThisArg, - bool hasInterpolatedStringRefMismatch) + bool hasInterpolatedStringRefMismatch, + bool dynamicConvertsToAnything) { // Spec 7.5.3.1 // For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical @@ -4195,7 +4212,9 @@ private Conversion CheckArgumentForApplicability( { var conversion = forExtensionMethodThisArg ? Conversions.ClassifyImplicitExtensionMethodThisArgConversion(argument, argument.Type, parameterType, ref useSiteInfo) : - Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteInfo); + ((!dynamicConvertsToAnything || !argument.Type.IsDynamic()) ? + Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteInfo) : + Conversion.ImplicitDynamic); Debug.Assert((!conversion.Exists) || conversion.IsImplicit, "ClassifyImplicitConversion should only return implicit conversions"); if (hasInterpolatedStringRefMismatch && !conversion.IsInterpolatedStringHandler) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index 188d1893ffebb..45c569b13deaa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -308,7 +308,7 @@ internal void ReportDiagnostics( // Otherwise, if there is any such method that has a bad argument conversion or out/ref mismatch // then the first such method found is the best bad method. - if (HadBadArguments(diagnostics, binder, name, arguments, symbols, location, binder.Flags, isMethodGroupConversion)) + if (HadBadArguments(diagnostics, binder, name, receiver, arguments, symbols, location, binder.Flags, isMethodGroupConversion)) { return; } @@ -519,8 +519,7 @@ internal void ReportDiagnostics( else { Debug.Assert(firstSupported.Member is MethodSymbol { Name: "Add" }); - int argumentOffset = arguments.IsExtensionMethodInvocation ? 1 : 0; - diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, arguments.Arguments[argumentOffset].Type, firstSupported.Member); + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, receiver.Type); } } else @@ -1081,6 +1080,7 @@ private bool HadBadArguments( BindingDiagnosticBag diagnostics, Binder binder, string name, + BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray symbols, Location location, @@ -1129,8 +1129,7 @@ private bool HadBadArguments( if (flags.Includes(BinderFlags.CollectionExpressionConversionValidation)) { - Debug.Assert(arguments.Arguments.Count == argumentOffset + 1); - diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, arguments.Arguments[argumentOffset].Type, method); + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, receiver.Type); } else { diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 522d191faa3db..775774fe0188c 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -207,7 +207,7 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI var argumentsBuilder = ArrayBuilder.GetInstance(disposeMethod.ParameterCount); ImmutableArray argsToParams = default; - originalBinder.BindDefaultArgumentsAndParamsCollection( + originalBinder.BindDefaultArguments( // If this is a using statement, then we want to use the whole `using (expr) { }` as the argument location. These arguments // will be represented in the IOperation tree and the "correct" node for them, given that they are an implicit invocation // at the end of the using statement, is on the whole using statement, not on the current expression. diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 1c597710b3960..fb6705753cfd4 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6863,7 +6863,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. The CollectionBuilderAttribute builder type must be a non-generic class or struct. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index ca0c4e3354ab1..ef6e145345c23 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -118,7 +118,7 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele if (element is BoundCollectionExpressionSpreadElement spreadElement) { Debug.Assert(spreadElement.IteratorBody is { }); - var iteratorBody = Binder.GetUnderlyingCollectionExpressionElement(node, ((BoundExpressionStatement)spreadElement.IteratorBody).Expression); + var iteratorBody = Binder.GetUnderlyingCollectionExpressionElement(node, ((BoundExpressionStatement)spreadElement.IteratorBody).Expression, throwOnErrors: true); Debug.Assert(iteratorBody is { }); return spreadElement.Update( spreadElement.Expression, @@ -131,7 +131,7 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele } else { - var result = Binder.GetUnderlyingCollectionExpressionElement(node, (BoundExpression)element); + var result = Binder.GetUnderlyingCollectionExpressionElement(node, (BoundExpression)element, throwOnErrors: true); Debug.Assert(result is { }); return result; } diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 9a43c65d82474..acf8246550b90 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1257,13 +1257,14 @@ private IOperation CreateBoundCollectionExpressionElement(BoundCollectionExpress { return element is BoundCollectionExpressionSpreadElement spreadElement ? CreateBoundCollectionExpressionSpreadElement(expr, spreadElement) : - Create(Binder.GetUnderlyingCollectionExpressionElement(expr, (BoundExpression)element) ?? element); + Create(Binder.GetUnderlyingCollectionExpressionElement(expr, (BoundExpression)element, throwOnErrors: false)); } private ISpreadOperation CreateBoundCollectionExpressionSpreadElement(BoundCollectionExpression expr, BoundCollectionExpressionSpreadElement element) { - var iteratorBody = ((BoundExpressionStatement?)element.IteratorBody)?.Expression; - var iteratorItem = Binder.GetUnderlyingCollectionExpressionElement(expr, iteratorBody); + var iteratorItem = element.IteratorBody is { } iteratorBody ? + Binder.GetUnderlyingCollectionExpressionElement(expr, ((BoundExpressionStatement)iteratorBody).Expression, throwOnErrors: false) : + null; var collection = Create(element.Expression); SyntaxNode syntax = element.Syntax; bool isImplicit = element.WasCompilerGenerated; diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 4d7e123e1468d..d5f5bcbe61e54 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -109,10 +109,12 @@ private ReducedExtensionMethodSymbol(MethodSymbol reducedFrom) /// are not satisfied, the return value is null. /// /// Compilation used to check constraints. The latest language version is assumed if this is null. - private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, + internal static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref CompoundUseSiteInfo useSiteInfo, out bool wasFullyInferred) { Debug.Assert(method.IsExtensionMethod); + Debug.Assert(method.MethodKind != MethodKind.ReducedExtension); + Debug.Assert(method.ParameterCount > 0); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 85eba6bdf14bb..27167398d80fe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -1579,7 +1579,7 @@ void validateParamsType(BindingDiagnosticBag diagnostics) checkIsAtLeastAsVisible(syntax, binder, constructor, diagnostics); } - if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, Type, elementType, out ImmutableArray addMethods, diagnostics)) + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, Type, out ImmutableArray addMethods, diagnostics)) { return; } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 52bc7a06848ef..2e38908bd80f0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 31fdb8ad5d94e..cb1ad8210d590 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 46d2302cf07e3..00a8f212f9370 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 21d95c543a69c..5a8985f99972b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 4b6f9d4393503..2c787d89e314c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d22e46da8f2c8..fce51cc05ff0d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3c3e4232b34f0..c0d26108c0b2d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 236da182cee77..5e77b34f3decb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ed4a82cea33ad..f23435934209d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 197239919e8d6..db6abdf3369e5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 44f14a30f0b80..9f9680b146df1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 0983b4f8fc93c..69dae0da412ef 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ac7f52744cd5c..2d423a4f77131 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -443,8 +443,8 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs index cbf51c6c062c2..b25606241d2b2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs @@ -651,7 +651,8 @@ class Program Assert.True(attributeData.HasErrors); Assert.Equal("AAttribute..ctor(params System.Int32[] args)", attributeData.AttributeConstructor.ToTestDisplayString()); Assert.Equal(1, attributeData.AttributeConstructor.ParameterCount); - Assert.Equal(new object[] { 1, 2, 3 }, attributeData.ConstructorArguments.Select(arg => arg.Value)); + Assert.Equal(TypedConstantKind.Array, attributeData.ConstructorArguments.Single().Kind); + Assert.Equal(new object[] { 1, 2, 3 }, attributeData.ConstructorArguments.Single().Values.Select(arg => arg.Value)); // `SourceAttributeData.GetAttributeArgumentSyntax` asserts in debug mode when the attributeData has errors, so we don't test it here. } @@ -1025,12 +1026,14 @@ class Program { } Assert.Equal(3, arguments0.Length); Assert.Equal(true, arguments0[0].Value); Assert.Equal(1, arguments0[1].Value); + Assert.Equal(TypedConstantKind.Array, arguments0[2].Kind); Assert.Empty(arguments0[2].Values); var arguments1 = attrs[1].ConstructorArguments.ToArray(); Assert.Equal(3, arguments1.Length); Assert.Equal(true, arguments1[0].Value); Assert.Equal(1, arguments1[1].Value); + Assert.Equal(TypedConstantKind.Array, arguments1[2].Kind); Assert.Equal("a", arguments1[2].Values.Single().Value); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index c44ae2e5e9ddc..c441c5042e758 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -3779,12 +3779,6 @@ static void Main() comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // 1.cs(6,23): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'List.Add(int)'. - // List l = [1]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "System.Collections.Generic.List.Add(int)").WithLocation(6, 23), - // 1.cs(6,23): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // List l = [1]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(6, 23), // 1.cs(7,16): error CS9174: Cannot initialize type 'IA' with a collection expression because the type is not constructible. // IA a = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.Collections.Generic.IA").WithLocation(7, 16), @@ -3849,12 +3843,6 @@ static void Main() comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // 1.cs(7,23): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'List.Add(int)'. - // List l = [1]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "System.Collections.Generic.List.Add(int)").WithLocation(7, 23), - // 1.cs(7,23): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // List l = [1]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(7, 23), // 1.cs(8,29): error CS9174: Cannot initialize type 'IEquatable' with a collection expression because the type is not constructible. // IEquatable e = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.IEquatable").WithLocation(8, 29)); @@ -4573,48 +4561,48 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(5, 21), - // (6,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(6, 21), - // (6,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(6, 21), - // (6,22): error CS8716: There is no target type for the default literal. - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 22), - // (7,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(7, 21), - // (7,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(7, 21), - // (7,22): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(7, 22), - // (8,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)['a']; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(8, 21), - // (8,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)['a']; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(8, 21), - // (9,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(9, 21), - // (9,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(9, 21), - // (9,22): error CS0029: Cannot implicitly convert type 'int' to 'char' - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(9, 22), - // (10,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[..""]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(10, 21), - // (10,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[..""]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(10, 21)); + // (5,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(5, 21), + // (6,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(6, 21), + // (6,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(6, 21), + // (6,22): error CS8716: There is no target type for the default literal. + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 22), + // (7,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(7, 21), + // (7,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(7, 21), + // (7,22): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(7, 22), + // (8,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)['a']; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(8, 21), + // (8,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)['a']; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(8, 21), + // (9,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(9, 21), + // (9,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(9, 21), + // (9,22): error CS0029: Cannot implicitly convert type 'int' to 'char' + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(9, 22), + // (10,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[..""]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(10, 21), + // (10,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[..""]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(10, 21)); } [Fact] @@ -5330,13 +5318,7 @@ public void Add(int i) { } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("C", "0").WithLocation(3, 5), // (4,5): error CS1729: 'C' does not contain a constructor that takes 0 arguments // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5), - // (4,5): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int)").WithLocation(4, 5), - // (4,5): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(4, 5)); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5)); } [WorkItem("https://github.com/dotnet/roslyn/pull/71492")] @@ -5408,17 +5390,13 @@ static void Main() object o; c = [1, 2]; o = (C)[3, 4]; + c.Report(); + o.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); - comp.VerifyEmitDiagnostics( - // 1.cs(7,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int)").WithLocation(7, 13), - // 1.cs(7,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(7, 13)); + CompileAndVerify(new[] { sourceA, sourceB2, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } [Fact] @@ -5525,36 +5503,30 @@ public void CollectionInitializerType_08A() string source = """ using System; using System.Collections; + using System.Collections.Generic; struct S0 : IEnumerable { - public void Add(T t) { } - IEnumerator IEnumerable.GetEnumerator() => throw null; + private List _list; + public void Add(T t) { GetList().Add(t); } + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + private List GetList() => _list ??= new(); } class Program { - static void M0() + static void Main() { object o = (S0)[]; + o.Report(); o = (S0)[1, 2]; + o.Report(); S0 s = []; + s.Report(); s = [1, 2]; + s.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (13,22): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S0.Add(int)'. - // o = (S0)[1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "S0.Add(int)").WithLocation(13, 22), - // (13,22): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // o = (S0)[1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(13, 22), - // (15,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S0.Add(int)'. - // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "S0.Add(int)").WithLocation(15, 13), - // (15,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // s = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(15, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [], [1, 2], "); } [Fact] @@ -5778,12 +5750,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (15,15): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(IA)'. + // (15,36): error CS0121: The call is ambiguous between the following methods or properties: 'C.Add(IA)' and 'C.Add(IB)' // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[(IA)null, (IB)null, new AB()]").WithArguments("object", "C.Add(IA)").WithLocation(15, 15), - // (15,15): error CS1503: Argument 1: cannot convert from 'object' to 'IA' - // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[(IA)null, (IB)null, new AB()]").WithArguments("1", "object", "IA").WithLocation(15, 15)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("C.Add(IA)", "C.Add(IB)").WithLocation(15, 36)); } [Fact] @@ -5813,12 +5782,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (18,15): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'Extensions.Add(C, IA)'. - // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[(IA)null, (IB)null, new AB()]").WithArguments("object", "Extensions.Add(C, IA)").WithLocation(18, 15), - // (18,15): error CS1503: Argument 2: cannot convert from 'object' to 'IA' + // (18,36): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions.Add(C, IA)' and 'Extensions.Add(C, IB)' // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[(IA)null, (IB)null, new AB()]").WithArguments("2", "object", "IA").WithLocation(18, 15)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("Extensions.Add(C, IA)", "Extensions.Add(C, IB)").WithLocation(18, 36)); } [Fact] @@ -5843,9 +5809,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(int, int)'. + // (13,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("object", "S.Add(int, int)").WithLocation(13, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(13, 13)); } [Fact] @@ -5872,9 +5838,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (15,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'S.Add(int, int)'. + // (15,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("int", "S.Add(int, int)").WithLocation(15, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(15, 13)); } [Fact] @@ -5904,9 +5870,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (18,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'Extensions.Add(S, T, T)'. + // (18,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("int", "Extensions.Add(S, T, T)").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(18, 13)); } [Fact] @@ -5926,18 +5892,13 @@ class Program static void Main() { C c = []; + c.Report(); c = [1, 2]; + c.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int, int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int, int)").WithLocation(14, 13), - // (14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(14, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] @@ -5984,18 +5945,13 @@ class Program static void Main() { C c = []; + c.Report(); c = [1, 2]; + c.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int, params int[])'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int, params int[])").WithLocation(14, 13), - // (14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(14, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] @@ -6112,18 +6068,12 @@ class Program """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,40): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(T)'. - // static S Create(T t, U u) => [t, u]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[t, u]").WithArguments("object", "S.Add(T)").WithLocation(7, 40), - // (7,40): error CS1503: Argument 1: cannot convert from 'object' to 'T' - // static S Create(T t, U u) => [t, u]; - Diagnostic(ErrorCode.ERR_BadArgType, "[t, u]").WithArguments("1", "object", "T").WithLocation(7, 40), - // (11,46): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(T)'. + // (11,50): error CS1950: The best overloaded Add method 'S.Add(T)' for the collection initializer has some invalid arguments // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, y]").WithArguments("object", "S.Add(T)").WithLocation(11, 46), - // (11,46): error CS1503: Argument 1: cannot convert from 'object' to 'T' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("S.Add(T)").WithLocation(11, 50), + // (11,50): error CS1503: Argument 1: cannot convert from 'U' to 'T' // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[x, y]").WithArguments("1", "object", "T").WithLocation(11, 46)); + Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "U", "T").WithLocation(11, 50)); } [Fact] @@ -6179,12 +6129,6 @@ class Program // (9,41): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S Create(T t, U u) => [t, u]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("T", "U").WithLocation(9, 41), - // (13,46): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'U'. The best overloaded method is 'S.Add(T)'. - // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, y]").WithArguments("U", "S.Add(T)").WithLocation(13, 46), - // (13,46): error CS1503: Argument 1: cannot convert from 'U' to 'T' - // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[x, y]").WithArguments("1", "U", "T").WithLocation(13, 46), // (13,47): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S Create(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(13, 47)); @@ -6695,9 +6639,9 @@ static void Main() // (16,42): error CS0411: The type arguments for method 'Extensions.Add(ref MyCollection, out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add(ref MyCollection, out T)").WithLocation(16, 42), - // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, out object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, out T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection z = [..x, ..y, 3]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, out object)").WithLocation(18, 34)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, out T)").WithLocation(18, 34)); } [Fact] @@ -6730,9 +6674,9 @@ static void Main() // (16,42): error CS0411: The type arguments for method 'Extensions.Add(ref MyCollection, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add(ref MyCollection, ref T)").WithLocation(16, 42), - // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection z = [..x, ..y, 3]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, ref object)").WithLocation(18, 34)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, ref T)").WithLocation(18, 34)); } [Fact] @@ -6837,12 +6781,12 @@ static void Main() // (20,21): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = new() { 3 }; Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(20, 21), - // (21,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'Extensions.Add(ref MyCollection, string)'. + // (21,14): error CS1950: The best overloaded Add method 'Extensions.Add(ref MyCollection, string)' for the collection initializer has some invalid arguments // y = [4]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[4]").WithArguments("int", "Extensions.Add(ref MyCollection, string)").WithLocation(21, 13), - // (21,13): error CS1503: Argument 2: cannot convert from 'int' to 'string' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "4").WithArguments("Extensions.Add(ref MyCollection, string)").WithLocation(21, 14), + // (21,14): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = [4]; - Diagnostic(ErrorCode.ERR_BadArgType, "[4]").WithArguments("2", "int", "string").WithLocation(21, 13)); + Diagnostic(ErrorCode.ERR_BadArgType, "4").WithArguments("2", "int", "string").WithLocation(21, 14)); } [Fact] @@ -6878,15 +6822,15 @@ static void Main() // (17,21): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = new() { "1" }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"""1""").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(17, 21), - // (18,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = ["2"]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"[""2""]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(18, 13), + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"[""2""]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(18, 13), // (20,21): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = new() { 3 }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "3").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(20, 21), - // (21,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (21,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = [4]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[4]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(21, 13)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[4]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(21, 13)); } [Fact] @@ -8040,13 +7984,13 @@ static void Main() comp = CreateCompilation(new[] { sourceC, s_collectionExtensions }, references: new[] { refB }); comp.VerifyEmitDiagnostics( - // 0.cs(6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = []; Diagnostic(ErrorCode.ERR_NoTypeDef, "[]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 13), - // 0.cs(8,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(8,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = [1, 2]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[1, 2]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 13), - // 0.cs(13,13): error CS0012: The type 'A2' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(13,14): error CS0012: The type 'A2' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // y = [3, 4]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[3, 4]").WithArguments("A2", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(13, 13)); } @@ -8147,12 +8091,12 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(int, int)'. + // (7,13): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // d = [default]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(int, int)").WithLocation(7, 13), - // (8,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(int, int)'. + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(7, 13), + // (8,13): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // d = [new KeyValuePair(1, 2)]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair(1, 2)]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(int, int)").WithLocation(8, 13), + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair(1, 2)]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(8, 13), // (9,15): error CS1003: Syntax error, ',' expected // d = [3:4]; Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",").WithLocation(9, 15), @@ -9433,18 +9377,42 @@ static void Main() if (targetElementType == "int") { comp.VerifyEmitDiagnostics( - // 1.cs(10,26): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(int)'. + // 1.cs(10,27): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..d1, ..d2, ..e1, ..e2]").WithArguments("object", "MyCollection.Add(int)").WithLocation(10, 26), - // 1.cs(10,26): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgType, "..d1").WithArguments("1", "object", "int").WithLocation(10, 27), + // 1.cs(10,29): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[..d1, ..d2, ..e1, ..e2]").WithArguments("1", "object", "int").WithLocation(10, 26), - // 1.cs(14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(int)'. + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d1").WithArguments("MyCollection.Add(int)").WithLocation(10, 29), + // 1.cs(10,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..d2").WithArguments("1", "object", "int").WithLocation(10, 33), + // 1.cs(10,35): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d2").WithArguments("MyCollection.Add(int)").WithLocation(10, 35), + // 1.cs(10,39): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..e1").WithArguments("1", "object", "int").WithLocation(10, 39), + // 1.cs(10,41): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e1").WithArguments("MyCollection.Add(int)").WithLocation(10, 41), + // 1.cs(10,45): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..e2").WithArguments("1", "object", "int").WithLocation(10, 45), + // 1.cs(10,47): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e2").WithArguments("MyCollection.Add(int)").WithLocation(10, 47), + // 1.cs(14,14): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // c = [..(dynamic)x, ..(IEnumerable)y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..(dynamic)x").WithArguments("1", "object", "int").WithLocation(14, 14), + // 1.cs(14,16): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // c = [..(dynamic)x, ..(IEnumerable)y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(dynamic)x").WithArguments("MyCollection.Add(int)").WithLocation(14, 16), + // 1.cs(14,28): error CS1503: Argument 1: cannot convert from 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..(dynamic)x, ..(IEnumerable)y]").WithArguments("object", "MyCollection.Add(int)").WithLocation(14, 13), - // 1.cs(14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgType, "..(IEnumerable)y").WithArguments("1", "object", "int").WithLocation(14, 28), + // 1.cs(14,30): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // c = [..(dynamic)x, ..(IEnumerable)y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[..(dynamic)x, ..(IEnumerable)y]").WithArguments("1", "object", "int").WithLocation(14, 13)); + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(IEnumerable)y").WithArguments("MyCollection.Add(int)").WithLocation(14, 30)); } else { @@ -25160,11 +25128,11 @@ .. GetConfig(), var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (4,52): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(string, object)'. + // (4,52): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // Dictionary Config => /**/[ Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, @"[ .. GetConfig(), - ]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(string, object)").WithLocation(4, 52)); + ]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(4, 52)); VerifyOperationTreeForTest(comp, """ @@ -25182,661 +25150,1231 @@ .. GetConfig(), } [Fact] - public void Async_01() + public void IOperation_AmbiguousAdd_01() { - string source = """ - using System.Collections.Generic; - using System.Threading.Tasks; - class Program + string sourceA = """ + using System.Collections; + interface IA { } + interface IB { } + class MyCollection : IEnumerable { - static async Task Main() - { - (await CreateArray()).Report(); - (await CreateList()).Report(); - } - static async Task CreateArray() - { - return [await F(1), await F(2)]; - } - static async Task> CreateList() - { - return [await F(3), await F(4)]; - } - static async Task F(int i) - { - await Task.Yield(); - return i; - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(IA a) => throw null; + public void Add(IB b) => throw null; + public void Add(object o) => throw null; } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); - } - - [Fact] - public void Async_02() - { - string source = """ - using System.Collections.Generic; - using System.Threading.Tasks; + string sourceB = """ + class C : IA, IB { } class Program { - static async Task Main() - { - (await F2(F1())).Report(); - } - static async Task F1() - { - return [await F(1), await F(2)]; - } - static async Task F2(Task e) - { - return [3, .. await e, 4]; - } - static async Task F(T t) + static MyCollection Create(C x, C[] y) { - await Task.Yield(); - return t; + return /**/[x, ..y]/**/; } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[3, 1, 2, 4], "); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void PostfixIncrementDecrement() + public void IOperation_AmbiguousAdd_02() { - string source = """ - using System.Collections.Generic; - - class Program + string sourceA = """ + using System.Collections; + interface IA { } + interface IB { } + class MyCollection : IEnumerable { - static void Main() - { - []++; - []--; - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(IA a) => throw null; + public void Add(IB b) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, object o) { } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer - // []++; - Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(7, 9), - // (8,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer - // []--; - Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(8, 9)); - } - - [Fact] - public void PostfixPointerAccess() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ + class C : IA, IB { } class Program { - static void Main() + static MyCollection Create(C x, C[] y) { - var v = []->Count; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,17): error CS9503: There is no target type for the collection expression. - // var v = []->Count; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void LeftHandAssignment() + public void IOperation_AmbiguousAdd_03() { - string source = """ + string sourceA = """ + using System.Collections; using System.Collections.Generic; - - class Program + class MyCollection : IEnumerable { - static void Main() + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + static class ExtensionsA + { + public static void Add(this MyCollection collection, string s) { } + } + static class ExtensionsB + { + public static void Add(this MyCollection collection, string s) { } + } + namespace N + { + static class ExtensionsC { - [] = null; + public static void Add(this MyCollection collection, T t) { } } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // [] = null; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[]").WithLocation(7, 9)); - } - - [Fact] - public void BinaryOperator() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ + using N; class Program { - static void Main(List list) + static MyCollection Create(string x, string[] y) { - [] + list; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS0019: Operator '+' cannot be applied to operands of type 'collection expressions' and 'List' - // [] + list; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "[] + list").WithArguments("+", "collection expressions", "System.Collections.Generic.List").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] + list; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] + list").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, string)' and 'ExtensionsB.Add(MyCollection, string)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("ExtensionsA.Add(MyCollection, string)", "ExtensionsB.Add(MyCollection, string)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, string)' and 'ExtensionsB.Add(MyCollection, string)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("ExtensionsA.Add(MyCollection, string)", "ExtensionsB.Add(MyCollection, string)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void RangeOperator() + public void IOperation_InvalidAdd_01() { - string source = """ - using System.Collections.Generic; - - class Program + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable { - static void Main(List list) - { - []..; - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(string s) { } } """; - CreateCompilationWithIndexAndRangeAndSpan(source).VerifyEmitDiagnostics( - // (7,9): error CS9500: Cannot initialize type 'Index' with a collection expression because the type is not constructible. - // []..; - Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("System.Index").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // []..; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[]..").WithLocation(7, 9)); - } - - [Fact] - public void TopLevelSwitchExpression() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create() { - [] switch { null => 0 }; + return /**/[F1(), ..F2()]/**/; } + static int F1() => 1; + static int[] F2() => [2, 3]; } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] switch - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] switch - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] switch { null => 0 }").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("MyCollection.Add(string)").WithLocation(5, 27), + // (5,27): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("1", "int", "string").WithLocation(5, 27), + // (5,33): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("1", "int", "string").WithLocation(5, 33), + // (5,35): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("MyCollection.Add(string)").WithLocation(5, 35)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelWithExpression() + public void IOperation_InvalidAdd_02() { - string source = """ - using System.Collections.Generic; - + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + public static class Extensions + { + public static void Add(this MyCollection collection, string s) { } + } + """; + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create() { - [] with { Count = 1, }; + return /**/[F1(), ..F2()]/**/; } + static int F1() => 1; + static int[] F2() => [2, 3]; } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] with { Count = 1, }; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] with { Count = 1, }; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] with { Count = 1, }").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(5, 27), + // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("2", "int", "string").WithLocation(5, 27), + // (5,33): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("2", "int", "string").WithLocation(5, 33), + // (5,35): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(5, 35)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelIsExpressions() + public void IOperation_InvalidAdd_03() { - string source = """ - using System.Collections.Generic; - + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + public static class Extensions + { + public static void Add(this MyCollection collection, params string[] args) { } + } + """; + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create(int x, int[] y) { - [] is object; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] is object; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] is object; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] is object").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, params string[])' for the collection initializer has some invalid arguments + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(MyCollection, params string[])").WithLocation(5, 27), + // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "string").WithLocation(5, 27), + // (5,30): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "string").WithLocation(5, 30), + // (5,32): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, params string[])' for the collection initializer has some invalid arguments + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions.Add(MyCollection, params string[])").WithLocation(5, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelAsExpressions() + public void IOperation_InvalidAdd_04() { - string source = """ - using System.Collections.Generic; - + string sourceA = """ + using System; + using System.Collections; + public class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + public Action Add; + } + """; + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create(int x, int[] y) { - [] as List; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] as List; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] as List; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] as List").WithLocation(7, 9)); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute() - { - string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,26): error CS0118: 'Add' is a field but is used like a method + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadSKknown, "[x, ..y]").WithArguments("Add", "field", "method").WithLocation(5, 26)); - [X([42, 43, 44])] - class C - { - } - - public class XAttribute : System.Attribute - { - public int[] _values; - public XAttribute(int[] values) { _values = values; } - } - """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + """); - var program = comp.GetMember("C"); - var argument = program.GetAttributes().Single().ConstructorArguments.Single(); - var values = argument.Values; - Assert.Equal(3, values.Length); - Assert.Equal(42, values[0].Value); - Assert.Equal(43, values[1].Value); - Assert.Equal(44, values[2].Value); + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_Named() + [Fact] + public void Async_01() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr.Values.Report(); - - [X(Values = [42, 43, 44])] - class C - { - } - - public class XAttribute : System.Attribute + using System.Collections.Generic; + using System.Threading.Tasks; + class Program { - public int[] Values { get; set; } + static async Task Main() + { + (await CreateArray()).Report(); + (await CreateList()).Report(); + } + static async Task CreateArray() + { + return [await F(1), await F(2)]; + } + static async Task> CreateList() + { + return [await F(3), await F(4)]; + } + static async Task F(int i) + { + await Task.Yield(); + return i; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_Params() + [Fact] + public void Async_02() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X([42, 43, 44])] - class C - { - } - - public class XAttribute : System.Attribute + using System.Collections.Generic; + using System.Threading.Tasks; + class Program { - public int[] _values; - public XAttribute(params int[] values) { _values = values; } + static async Task Main() + { + (await F2(F1())).Report(); + } + static async Task F1() + { + return [await F(1), await F(2)]; + } + static async Task F2(Task e) + { + return [3, .. await e, 4]; + } + static async Task F(T t) + { + await Task.Yield(); + return t; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[3, 1, 2, 4], "); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_StringConstants() + [Fact] + public void PostfixIncrementDecrement() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X(["hi", null])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public string[] _values; - public XAttribute(string[] values) { _values = values; } + static void Main() + { + []++; + []--; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[hi, null],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // []++; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(7, 9), + // (8,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // []--; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(8, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NestedArray() + [Fact] + public void PostfixPointerAccess() { string source = """ - [X([[1], [2]])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public XAttribute(int[][] values) { } + static void Main() + { + var v = []->Count; + } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,2): error CS0181: Attribute constructor parameter 'values' has type 'int[][]', which is not a valid attribute parameter type - // [X([[1], [2]])] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("values", "int[][]").WithLocation(1, 2) - ); + // (7,17): error CS9503: There is no target type for the collection expression. + // var v = []->Count; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NestedArrayAsObject() + [Fact] + public void LeftHandAssignment() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - var inner = (int[])attr._values[0]; - inner.Report(); - - [X([(int[])[1]])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public object[] _values; - public XAttribute(object[] values) { _values = values; } + static void Main() + { + [] = null; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // [] = null; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[]").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_ArrayAsObject() + [Fact] + public void BinaryOperator() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - var array = (int[])attr._value; - array.Report(); - - [X((int[])[1, 2, 3])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public object _value; - public XAttribute(object value) { _value = value; } + static void Main(List list) + { + [] + list; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS0019: Operator '+' cannot be applied to operands of type 'collection expressions' and 'List' + // [] + list; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "[] + list").WithArguments("+", "collection expressions", "System.Collections.Generic.List").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] + list; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] + list").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_Empty() + [Fact] + public void RangeOperator() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X([])] - class C + using System.Collections.Generic; + + class Program { - } - - public class XAttribute : System.Attribute - { - public int[] _values; - public XAttribute(int[] values) { _values = values; } + static void Main(List list) + { + []..; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[],"); + CreateCompilationWithIndexAndRangeAndSpan(source).VerifyEmitDiagnostics( + // (7,9): error CS9500: Cannot initialize type 'Index' with a collection expression because the type is not constructible. + // []..; + Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("System.Index").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // []..; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[]..").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NotConstant() + [Fact] + public void TopLevelSwitchExpression() { string source = """ - [X([1, 2, C.M()])] - class C + using System.Collections.Generic; + + class Program { - public static int M() => 0; + static void Main(List list) + { + [] switch { null => 0 }; + } } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] switch + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] switch + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] switch { null => 0 }").WithLocation(7, 9)); + } - public class XAttribute : System.Attribute + [Fact] + public void TopLevelWithExpression() + { + string source = """ + using System.Collections.Generic; + + class Program { - public XAttribute(int[] values) { } + static void Main(List list) + { + [] with { Count = 1, }; + } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,11): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type - // [X([1, 2, C.M()])] - Diagnostic(ErrorCode.ERR_BadAttributeArgument, "C.M()").WithLocation(1, 11) - ); + // (7,9): error CS9503: There is no target type for the collection expression. + // [] with { Count = 1, }; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] with { Count = 1, }; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] with { Count = 1, }").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NotConstant_CollectionSpread() + [Fact] + public void TopLevelIsExpressions() { string source = """ - [X([1, 2, .. [3]])] - class C + using System.Collections.Generic; + + class Program { + static void Main(List list) + { + [] is object; + } } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] is object; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] is object; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] is object").WithLocation(7, 9)); + } - public class XAttribute : System.Attribute + [Fact] + public void TopLevelAsExpressions() + { + string source = """ + using System.Collections.Generic; + + class Program { - public XAttribute(int[] values) { } + static void Main(List list) + { + [] as List; + } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,14): error CS9176: There is no target type for the collection expression. - // [X([1, 2, .. [3]])] - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(1, 14) - ); + // (7,9): error CS9503: There is no target type for the collection expression. + // [] as List; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] as List; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] as List").WithLocation(7, 9)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NotConstant_ListSpread() + public void InAttribute() { string source = """ - using System.Collections.Generic; + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); - [X([.. new List()])] + [X([42, 43, 44])] class C { } public class XAttribute : System.Attribute { - public XAttribute(int[] values) { } + public int[] _values; + public XAttribute(int[] values) { _values = values; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (3,5): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type - // [X([.. new List()])] - Diagnostic(ErrorCode.ERR_BadAttributeArgument, ".. new List()").WithLocation(3, 5) - ); + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + + var program = comp.GetMember("C"); + var argument = program.GetAttributes().Single().ConstructorArguments.Single(); + var values = argument.Values; + Assert.Equal(3, values.Length); + Assert.Equal(42, values[0].Value); + Assert.Equal(43, values[1].Value); + Assert.Equal(44, values[2].Value); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_BadArrayType() + public void InAttribute_Named() { string source = """ - [X([1])] + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr.Values.Report(); + + [X(Values = [42, 43, 44])] class C { } public class XAttribute : System.Attribute { - public XAttribute(ERROR[] values) { } + public int[] Values { get; set; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,4): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'ERROR[]' - // [X([1])] - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "collection expressions", "ERROR[]").WithLocation(1, 4), - // (8,23): error CS0246: The type or namespace name 'ERROR' could not be found (are you missing a using directive or an assembly reference?) - // public XAttribute(ERROR[] values) { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ERROR").WithArguments("ERROR").WithLocation(8, 23) - ); + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NotArrayType() + public void InAttribute_Params() { string source = """ - [X([1])] + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X([42, 43, 44])] class C { } public class XAttribute : System.Attribute { - public XAttribute(int NOT_ARRAY) { } + public int[] _values; + public XAttribute(params int[] values) { _values = values; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,4): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'int' - // [X([1])] - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "collection expressions", "int").WithLocation(1, 4) - ); + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_SpanType() + public void InAttribute_StringConstants() { string source = """ - [X([1])] + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X(["hi", null])] class C { } public class XAttribute : System.Attribute { - public XAttribute(System.Span s) { } + public string[] _values; + public XAttribute(string[] values) { _values = values; } } """; - CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)).VerifyEmitDiagnostics( - // (1,2): error CS0181: Attribute constructor parameter 's' has type 'Span', which is not a valid attribute parameter type - // [X([1])] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.Span").WithLocation(1, 2), - // (1,4): warning CS9208: Collection expression of type 'Span' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'Span' to make the allocation explicit. - // [X([1])] - Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[1]").WithArguments("System.Span").WithLocation(1, 4) - ); + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[hi, null],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_ReadOnlySpanType() + public void InAttribute_NestedArray() { string source = """ - [X([1])] + [X([[1], [2]])] class C { } public class XAttribute : System.Attribute { - public XAttribute(System.ReadOnlySpan s) { } + public XAttribute(int[][] values) { } } """; - CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( - // (1,2): error CS0181: Attribute constructor parameter 's' has type 'ReadOnlySpan', which is not a valid attribute parameter type - // [X([1])] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.ReadOnlySpan").WithLocation(1, 2) + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,2): error CS0181: Attribute constructor parameter 'values' has type 'int[][]', which is not a valid attribute parameter type + // [X([[1], [2]])] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("values", "int[][]").WithLocation(1, 2) ); } - [Fact] - public void InAttribute_CollectionBuilderType() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NestedArrayAsObject() { - string sourceA = """ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Runtime.CompilerServices; + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + var inner = (int[])attr._values[0]; + inner.Report(); - [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] - public struct MyCollection : IEnumerable + [X([(int[])[1]])] + class C { - private readonly List _list; - public MyCollection(List list) { _list = list; } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public class MyCollectionBuilder + + public class XAttribute : System.Attribute { - public static MyCollection Create(ReadOnlySpan items) - { - return new MyCollection(new List(items.ToArray())); - } + public object[] _values; + public XAttribute(object[] values) { _values = values; } } + """; - [X([1])] + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_ArrayAsObject() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + var array = (int[])attr._value; + array.Report(); + + [X((int[])[1, 2, 3])] class C { } public class XAttribute : System.Attribute { - public XAttribute(MyCollection s) { } + public object _value; + public XAttribute(object value) { _value = value; } } """; - var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics( + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_Empty() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X([])] + class C + { + } + + public class XAttribute : System.Attribute + { + public int[] _values; + public XAttribute(int[] values) { _values = values; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NotConstant() + { + string source = """ + [X([1, 2, C.M()])] + class C + { + public static int M() => 0; + } + + public class XAttribute : System.Attribute + { + public XAttribute(int[] values) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,11): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [X([1, 2, C.M()])] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "C.M()").WithLocation(1, 11) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NotConstant_CollectionSpread() + { + string source = """ + [X([1, 2, .. [3]])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(int[] values) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,14): error CS9176: There is no target type for the collection expression. + // [X([1, 2, .. [3]])] + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(1, 14) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NotConstant_ListSpread() + { + string source = """ + using System.Collections.Generic; + + [X([.. new List()])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(int[] values) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (3,5): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [X([.. new List()])] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, ".. new List()").WithLocation(3, 5) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_BadArrayType() + { + string source = """ + [X([1])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(ERROR[] values) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,4): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'ERROR[]' + // [X([1])] + Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "collection expressions", "ERROR[]").WithLocation(1, 4), + // (8,23): error CS0246: The type or namespace name 'ERROR' could not be found (are you missing a using directive or an assembly reference?) + // public XAttribute(ERROR[] values) { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ERROR").WithArguments("ERROR").WithLocation(8, 23) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NotArrayType() + { + string source = """ + [X([1])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(int NOT_ARRAY) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,4): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'int' + // [X([1])] + Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "collection expressions", "int").WithLocation(1, 4) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_SpanType() + { + string source = """ + [X([1])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(System.Span s) { } + } + """; + + CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)).VerifyEmitDiagnostics( + // (1,2): error CS0181: Attribute constructor parameter 's' has type 'Span', which is not a valid attribute parameter type + // [X([1])] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.Span").WithLocation(1, 2), + // (1,4): warning CS9208: Collection expression of type 'Span' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'Span' to make the allocation explicit. + // [X([1])] + Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[1]").WithArguments("System.Span").WithLocation(1, 4) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_ReadOnlySpanType() + { + string source = """ + [X([1])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(System.ReadOnlySpan s) { } + } + """; + + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (1,2): error CS0181: Attribute constructor parameter 's' has type 'ReadOnlySpan', which is not a valid attribute parameter type + // [X([1])] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.ReadOnlySpan").WithLocation(1, 2) + ); + } + + [Fact] + public void InAttribute_CollectionBuilderType() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] + public struct MyCollection : IEnumerable + { + private readonly List _list; + public MyCollection(List list) { _list = list; } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + public class MyCollectionBuilder + { + public static MyCollection Create(ReadOnlySpan items) + { + return new MyCollection(new List(items.ToArray())); + } + } + + [X([1])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(MyCollection s) { } + } + """; + + var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics( // (22,2): error CS0181: Attribute constructor parameter 's' has type 'MyCollection', which is not a valid attribute parameter type // [X([1])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "MyCollection").WithLocation(22, 2) @@ -26035,13 +26573,7 @@ public static void M(int[] i) { } var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (3,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(3, 7), - // (3,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "C.Add(int)").WithLocation(3, 7), - // (3,7): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(3, 7) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(3, 7) ); var tree = comp.SyntaxTrees.First(); @@ -26084,13 +26616,7 @@ public static void M(int[] i) { } var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(4, 7), - // (4,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..values]").WithArguments("object", "C.Add(int)").WithLocation(4, 7), - // (4,7): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[..values]").WithArguments("1", "object", "int").WithLocation(4, 7) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(4, 7) ); var tree = comp.SyntaxTrees.First(); @@ -26131,12 +26657,6 @@ public void Add(int i) { } // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(4, 7), - // (4,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'string'. The best overloaded method is 'C.Add(int)'. - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("string", "C.Add(int)").WithLocation(4, 7), - // (4,7): error CS1503: Argument 1: cannot convert from 'string' to 'int' - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "string", "int").WithLocation(4, 7), // (4,8): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(4, 8) @@ -26176,12 +26696,6 @@ public static void M(int[] i) { } // (5,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(5, 7), - // (5,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'string'. The best overloaded method is 'C.Add(int)'. - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..values]").WithArguments("string", "C.Add(int)").WithLocation(5, 7), - // (5,7): error CS1503: Argument 1: cannot convert from 'string' to 'int' - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[..values]").WithArguments("1", "string", "int").WithLocation(5, 7), // (5,10): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "values").WithArguments("int", "string").WithLocation(5, 10) @@ -26874,20 +27388,11 @@ class MyCollection2 : IEnumerable where TAdd : TElemen } """; - var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); - comp.VerifyEmitDiagnostics( - // 0.cs(4,24): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection1.Add(int)'. - // MyCollection1 x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("object", "MyCollection1.Add(int)").WithLocation(4, 24), - // 0.cs(4,24): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // MyCollection1 x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "object", "int").WithLocation(4, 24), - // 0.cs(6,32): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection2.Add(int)'. - // MyCollection2 y = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("object", "MyCollection2.Add(int)").WithLocation(6, 32), - // 0.cs(6,32): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // MyCollection2 y = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "object", "int").WithLocation(6, 32)); + CompileAndVerify( + new[] { source, s_collectionExtensions }, + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify, + expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); } [Fact] @@ -26910,19 +27415,19 @@ class MyCollection2 : IEnumerable where TAdd : TElemen var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (4,32): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection2.Add(int)'. + // (4,33): error CS1950: The best overloaded Add method 'MyCollection2.Add(int)' for the collection initializer has some invalid arguments // MyCollection2 y = [new object()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new object()]").WithArguments("object", "MyCollection2.Add(int)").WithLocation(4, 32), - // (4,32): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "new object()").WithArguments("MyCollection2.Add(int)").WithLocation(4, 33), + // (4,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection2 y = [new object()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[new object()]").WithArguments("1", "object", "int").WithLocation(4, 32) + Diagnostic(ErrorCode.ERR_BadArgType, "new object()").WithArguments("1", "object", "int").WithLocation(4, 33) ); } [Fact] public void GenericIEnumerable_DifferentConversionToAdd() { - // For purpose of conversion, we rely on conversion from numeric literal to uint (from IEnumerable) + // For purpose of conversion, we rely on the existence of an Add method. // But for purpose of construction, we rely on conversion from numeric literal to sbyte (from Add(sbyte)) string source = """ using System.Collections; @@ -26940,21 +27445,18 @@ class MyCollection : IEnumerable } """; - var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); - comp.VerifyEmitDiagnostics( - // 0.cs(4,18): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'uint'. The best overloaded method is 'MyCollection.Add(sbyte)'. - // MyCollection x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("uint", "MyCollection.Add(sbyte)").WithLocation(4, 18), - // 0.cs(4,18): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' - // MyCollection x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "uint", "sbyte").WithLocation(4, 18)); + CompileAndVerify( + new[] { source, s_collectionExtensions }, + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify, + expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); } [Fact] public void GenericIEnumerable_NoConversionToAdd() { - // For purpose of conversion, we rely on conversion from numeric literal to uint (from IEnumerable) - // But for purpose of construction, we rely on conversion from numeric literal to sbyte (from Add(sbyte)) + // For purpose of conversion, we rely on the existence of an Add method. + // But for purpose of construction, there is no conversion from uint to sbyte (from Add(sbyte)) string source = """ using System.Collections; using System.Collections.Generic; @@ -26972,12 +27474,12 @@ class MyCollection : IEnumerable var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // 0.cs(4,18): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'uint'. The best overloaded method is 'MyCollection.Add(sbyte)'. + // 0.cs(4,19): error CS1950: The best overloaded Add method 'MyCollection.Add(sbyte)' for the collection initializer has some invalid arguments // MyCollection x = [uint.MaxValue]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[uint.MaxValue]").WithArguments("uint", "MyCollection.Add(sbyte)").WithLocation(4, 18), - // 0.cs(4,18): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "uint.MaxValue").WithArguments("MyCollection.Add(sbyte)").WithLocation(4, 19), + // 0.cs(4,19): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' // MyCollection x = [uint.MaxValue]; - Diagnostic(ErrorCode.ERR_BadArgType, "[uint.MaxValue]").WithArguments("1", "uint", "sbyte").WithLocation(4, 18) + Diagnostic(ErrorCode.ERR_BadArgType, "uint.MaxValue").WithArguments("1", "uint", "sbyte").WithLocation(4, 19) ); } @@ -27194,12 +27696,9 @@ class Collection : IEnumerable """; CreateCompilation(source).VerifyEmitDiagnostics( - // (4,16): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'Collection.Add(I1)'. - // Collection c = [new C()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new C()]").WithArguments("object", "Collection.Add(I1)").WithLocation(4, 16), - // (4,16): error CS1503: Argument 1: cannot convert from 'object' to 'I1' + // (4,17): error CS0121: The call is ambiguous between the following methods or properties: 'Collection.Add(I1)' and 'Collection.Add(I2)' // Collection c = [new C()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[new C()]").WithArguments("1", "object", "I1").WithLocation(4, 16)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new C()").WithArguments("Collection.Add(I1)", "Collection.Add(I2)").WithLocation(4, 17)); } [Fact] @@ -33418,714 +33917,3816 @@ .locals init (int V_0, IL_00a1: call "void CollectionExtensions.Report(object, bool)" IL_00a6: ret } - """); + """); + } + + [Fact] + public void TypeInference_LambdaReturn() + { + var source = """ + #nullable enable + using System; + class Program + { + static void Main() + { + object x = new object(); + object y = null; + object[] z = new[] { x }; + F(z, () => [x]); + F(z, () => [y]); + } + static void F(T t, Func f) { } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,20): warning CS8600: Converting null literal or possible null value to non-nullable type. + // object y = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 20), + // (11,21): warning CS8601: Possible null reference assignment. + // F(z, () => [y]); + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(11, 21)); + } + + [Fact] + public void TargetTypedElement_PublicAPI_List() + { + var source = """ + using System.Collections.Generic; + class C + { + static void Main() + { + List items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_Array() + { + var source = """ + class C + { + static void Main() + { + object[] items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_Span() + { + var source = """ + using System; + + class C + { + static void Main() + { + Span items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ReadOnlySpan() + { + var source = """ + using System; + + class C + { + static void Main() + { + ReadOnlySpan items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.ReadOnlySpan) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ImmutableArray() + { + var source = """ + using System.Collections.Immutable; + + class C + { + static void Main() + { + ImmutableArray items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + 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()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_IEnumerableT() + { + var source = """ + using System.Collections.Generic; + + class C + { + static void Main() + { + IEnumerable items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ImplementsIEnumerable() + { + var source = """ + using System.Collections; + + class C + { + static void Main() + { + MyCollection items = [new()]; + } + } + + class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null!; + public void Add(object obj) { } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[new()]') + 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) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_01() + { + string source = """ + using System; + using System.Collections.Generic; + static class Extensions + { + public static void Add(this ICollection collection, params T[] elements) + { + foreach (T element in elements) + collection.Add(element); + } + } + class Program + { + static Dictionary CreateDictionary(ICollection> collection) + { + return /**/[..collection]/**/; + } + static void Main() + { + var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; + var d = CreateDictionary(v); + foreach (var kvp in d) + Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), "); + + verifier.VerifyIL("Extensions.Add(this System.Collections.Generic.ICollection, params T[])", """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (T[] V_0, + int V_1, + T V_2) //element + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0019 + IL_0006: ldloc.0 + IL_0007: ldloc.1 + IL_0008: ldelem "T" + IL_000d: stloc.2 + IL_000e: ldarg.0 + IL_000f: ldloc.2 + IL_0010: callvirt "void System.Collections.Generic.ICollection.Add(T)" + IL_0015: ldloc.1 + IL_0016: ldc.i4.1 + IL_0017: add + IL_0018: stloc.1 + IL_0019: ldloc.1 + IL_001a: ldloc.0 + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: blt.s IL_0006 + IL_001f: ret + } + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') + Elements(1): + ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') + Operand: + IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsCollection_01() + { + string source = """ + using System; + using System.Collections.Generic; + static class Extensions + { + public static void Add(this ICollection collection, params IEnumerable elements) + { + foreach (T element in elements) + collection.Add(element); + } + } + class Program + { + static Dictionary CreateDictionary(ICollection> collection) + { + return /**/[..collection]/**/; + } + static void Main() + { + var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; + var d = CreateDictionary(v); + foreach (var kvp in d) + Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), ").VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') + Elements(1): + ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') + Operand: + IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params T[] x) => _list.AddRange(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_03() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __AddRange(T[] x) { _list.AddRange(x); } + } + static class Extensions + { + public static void Add(this MyCollection c, params T[] x) { c.__AddRange(x); } + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [Fact] + public void Add_ParamsArray_04() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(T x, params T[] y) => _list.Add(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [Fact] + public void Add_ParamsArray_05() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __Add(T x) { _list.Add(x); } + } + static class Extensions + { + public static void Add(this MyCollection c, T x, params T[] y) { c.__Add(x); } + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_06() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params MyCollection[] x) + { + Console.Write("Add: "); + x.Report(); + Console.WriteLine(); + _list.AddRange(x); + } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + MyCollection x = []; + MyCollection[] y = []; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: [[]], + [[]], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') + ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = /**/[[]]/**/; + x.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: [], + [], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection[], IsImplicit) (Syntax: '[]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection[]) (Syntax: '[]') + Elements(0) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + struct MyCollection : IEnumerable + { + private List _list; + public void Add(params MyCollection?[] x) => GetList().AddRange(x); + public IEnumerator GetEnumerator() => GetList().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private List GetList() => _list ??= new(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + MyCollection x = []; + MyCollection[] y = []; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[[]], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') + ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (ImplicitNullable) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection? x = /**/[[]]/**/; + x.Value.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?[], IsImplicit) (Syntax: '[]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection?[]) (Syntax: '[]') + Elements(0) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_08() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params object[] x) + { + Console.Write("Add: "); + foreach (var i in x) + Console.Write("{0}, ", i); + Console.WriteLine(); + _list.AddRange(x); + } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: 1, + Add: 2, + Add: 3, + [1, 2, 3], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + object[] x = [1]; + object[][] y = [[2, 3]]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: 1, + Add: 2, 3, + [1, 2, 3], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object[]) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[][]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_09() + { + string sourceA1 = """ + public abstract class MyCollectionBase + { + public abstract void Add(object[] x); + } + """; + string assemblyName = GetUniqueName(); + var comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(1, 0, 0, 0)), sourceA1, references: TargetFrameworkUtil.StandardReferences); + var refA1 = comp.EmitToImageReference(); + + string sourceB = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : MyCollectionBase, IEnumerable + { + private List _list = new(); + public override void Add(object[] x) => _list.AddRange(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + comp = CreateCompilation(sourceB, references: [refA1]); + var refB = comp.EmitToImageReference(); + + string sourceA2 = """ + public abstract class MyCollectionBase + { + public abstract void Add(params object[] x); + } + """; + comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(2, 0, 0, 0)), sourceA2, references: TargetFrameworkUtil.StandardReferences); + var refA2 = comp.EmitToImageReference(); + + string sourceC = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refA2, refB], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Element { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + ElementCollection c = [new Element(), null]; + c.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Base { } + class Element : Base { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + ElementCollection c = [new Element(), null]; + c.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/71240")] + [Fact] + public void AddMethod_Derived_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + + class Sample : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { if (t is object[] o) _list.Add(o); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + Sample s = [["a"], ["b"], ["c"]]; + s.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[[a], [b], [c]], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + Sample s = ["a", null]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,29): error CS0029: Cannot implicitly convert type 'string' to 'object[]' + // Sample s = ["a", null]; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""a""").WithArguments("string", "object[]").WithLocation(5, 29)); + } + + [Fact] + public void AddMethod_Accessibility_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + partial class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private void Add(int i) { _list.Add(i is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0122: 'MyCollection.Add(int)' is inaccessible due to its protection level + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadAccess, "[x, ..y]").WithArguments("MyCollection.Add(int)").WithLocation(7, 34)); + + string sourceB2 = """ + partial class MyCollection + { + public static void Run() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + class Program + { + static void Main() + { + MyCollection.Run(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_Accessibility_02() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __AddInternal(int i) { _list.Add(i is T t ? t : default); } + } + internal static class Extensions + { + public static void Add(this MyCollection c, int i) { c.__AddInternal(i); } + } + """; + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + var comp = CreateCompilation(sourceA); + var refA = comp.ToMetadataReference(); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 34)); + } + + [Fact] + public void AddMethod_Accessibility_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollectionBase : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + protected void Add(T t) { _list.Add(t); } + } + class MyCollection : MyCollectionBase + { + internal void __AddInternal(T t) { Add(t); } + } + """; + string sourceB = """ + static class Extensions + { + public static void Add(this MyCollection c, T t) { c.__AddInternal(t); } + } + """; + string sourceC = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + var comp = CreateCompilation([sourceA, sourceC, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0122: 'MyCollectionBase.Add(object)' is inaccessible due to its protection level + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadAccess, "[x, ..y]").WithArguments("MyCollectionBase.Add(object)").WithLocation(7, 34)); + } + + [Fact] + public void AddMethod_Overloads() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(string s) { _list.Add(s is T t ? t : default); } + public void Add(int i) { _list.Add(i is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + string x = "1"; + string[] y = ["2", "3"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB3 = """ + class Program + { + static void Main() + { + int? x = 1; + int?[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB3]); + comp.VerifyEmitDiagnostics( + // (7,33): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(7, 33), + // (7,33): error CS1503: Argument 1: cannot convert from 'int?' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int?", "string").WithLocation(7, 33), + // (7,36): error CS1503: Argument 1: cannot convert from 'int?' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int?", "string").WithLocation(7, 36), + // (7,38): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(7, 38)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_01(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add({{refKind}} T t) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add({{refKind}} T t) { t = default; _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,32): error CS1954: The best overloaded method match 'MyCollection.Add(ref int?)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[x, ..y, null]").WithArguments("MyCollection.Add(" + refKind + " int?)").WithLocation(15, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(out IA a) => throw null; + public void Add(ref IB b) => throw null; + public void Add({{refKind}} IC c) { _list.Add(c); } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_01(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1954: The best overloaded method match 'Extensions.Add(MyCollection, ref T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[x, ..y, null]").WithArguments("Extensions.Add(MyCollection, " + refKind + " T)").WithLocation(19, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_04(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (21,33): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(R)").WithLocation(21, 33), + // (21,33): error CS1503: Argument 1: cannot convert from 'int' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "R").WithLocation(21, 33), + // (21,36): error CS1503: Argument 1: cannot convert from 'int' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "R").WithLocation(21, 36), + // (21,38): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(R)").WithLocation(21, 38), + // (21,41): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("MyCollection.Add(R)").WithLocation(21, 41), + // (21,41): error CS1503: Argument 1: cannot convert from '' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "R").WithLocation(21, 41)); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_05(string refKind) + { + string source = $$""" + using N; + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + namespace N + { + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_06(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(out IA a) => throw null; + public void Add(ref IB b) => throw null; + internal void __AddInternal(IC c) { _list.Add(c); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} IC c) { collection.__AddInternal(c); } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_07(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + using N; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(IC c) { _list.Add(c); } + } + static class Extensions + { + public static void Add(this MyCollection collection, out IA a) => throw null; + public static void Add(this MyCollection collection, ref IB b) => throw null; + } + namespace N + { + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} IC c) { collection.__AddInternal(c); } + } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add() { _list.Add(default); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,32): error CS1501: No overload for method 'Add' takes 1 arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgCount, "[x, ..y, null]").WithArguments("Add", "1").WithLocation(15, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_02A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, {{refKind}} int x = 1, {{refKind}} int y = 2) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_02B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T t, int x = 1); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T t, int x) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'MyCollection.Add(int?, int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("x", "MyCollection.Add(int?, int)").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_02C() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T t, int x); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T t, int x = 1) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [InlineData("")] + [InlineData("in ")] + [InlineData("ref readonly ")] + public void AddMethod_03A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, {{refKind}} int x, {{refKind}} int y = 2) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + if (refKind == "ref readonly ") + { + comp.VerifyEmitDiagnostics( + // (7,69): warning CS9200: A default value is specified for 'ref readonly' parameter 'y', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // public void Add(T t, ref readonly int x, ref readonly int y = 2) { _list.Add(t); } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "2").WithArguments("y").WithLocation(7, 69), + // (15,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 32)); + } + else + { + comp.VerifyEmitDiagnostics( + // (15,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 32)); + } + } + + [Theory] + [CombinatorialData] + public void AddMethod_03B(bool useOut) + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(T t, ref int index = 0) => throw null; + // } + string sourceA = $$""" + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(!T t, {{(useOut ? "[out]" : "")}} [opt] int32& index) + { + .param [2] = int32(0x00000000) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31), + // (8,39): error CS7036: There is no argument given that corresponds to the required parameter 'index' of 'MyCollection.Add(int, ref int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("index", $"MyCollection.Add(int, {(useOut ? "out" : "ref")} int)").WithLocation(8, 39)); + } + + [Fact] + public void AddMethod_04A() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(params T[] args); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T[] args) + { + if (args is null) return; + _list.AddRange(args); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = new() { (int?)null, null }; + MyCollection y = [(int?)null, null]; + x.Report(); + y.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[null], [null], "); + } + + [Fact] + public void AddMethod_04B() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T[] args); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(params T[] args) + { + if (args is null) return; + _list.AddRange(args); + } + } + """; + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB]); + comp.VerifyEmitDiagnostics( + // (7,33): error CS1950: The best overloaded Add method 'MyCollection.Add(int?[])' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(int?[])").WithLocation(7, 33), + // (7,33): error CS1503: Argument 1: cannot convert from 'int' to 'int?[]' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "int?[]").WithLocation(7, 33), + // (7,36): error CS1503: Argument 1: cannot convert from 'int' to 'int?[]' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "int?[]").WithLocation(7, 36), + // (7,38): error CS1950: The best overloaded Add method 'MyCollection.Add(int?[])' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(int?[])").WithLocation(7, 38)); + } + + [Fact] + public void AddMethod_05A() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, params T[] y); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T[] y) { _list.Add(x); _list.AddRange(y); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_05B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T[] y); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, params T[] y) { _list.Add(x); _list.AddRange(y); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + + [Fact] + public void AddMethod_06A() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T y = default, params T[] z); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T y, T[] z) { _list.Add(x); _list.Add(y); _list.AddRange(z); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'MyCollection.Add(int?, int?, params int?[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("y", "MyCollection.Add(int?, int?, params int?[])").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_06B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T y, T[] z); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T y = default, params T[] z) { _list.Add(x); _list.Add(y); _list.AddRange(z); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'z' of 'MyCollection.Add(int?, int?, int?[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("z", "MyCollection.Add(int?, int?, int?[])").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(string s) { } + internal void __AddInternal(T t) { _list.Add(t); } + } + namespace N + { + internal static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + """; + var comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + + string sourceB = """ + using N; + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (8,32): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(8, 32), + // (8,32): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "string").WithLocation(8, 32), + // (8,35): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "string").WithLocation(8, 35), + // (8,37): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(8, 37)); + } + + [Fact] + public void AddMethod_08() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(string s) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, int i) { collection.__AddInternal(i); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceB2, sourceA]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 1: cannot convert from 'object' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "object", "string").WithLocation(7, 35), + // (7,38): error CS1503: Argument 1: cannot convert from 'object' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "object", "string").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(7, 40)); + } + + [Fact] + public void AddMethod_09() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(T x, params T y) => throw null; + // } + string sourceA = $$""" + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(!T x, !T y) + { + .param [2] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31), + // (8,39): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'MyCollection.Add(int, params int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("y", "MyCollection.Add(int, params int)").WithLocation(8, 39)); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T x, params List y) + { + _list.Add(x); + foreach (var i in y) + _list.Add(i); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_02() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U x, params MyCollection y) where U : T + { + _list.Add(x); + foreach (var i in y) + _list.Add(i); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_03() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(object x, [ParamCollection] object y) => throw null; + // } + string sourceA = $$""" + .class public System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + } + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(object x, object y) + { + .param [2] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31)); + } + + [Fact] + public void AddMethod_Extension_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection) { collection.__AddInternal(default); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1501: No overload for method 'Add' takes 1 arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgCount, "[x, ..y, null]").WithArguments("Add", "1").WithLocation(19, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_Extension_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t, {{refKind}} int x = 1, {{refKind}} int y = 2) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_Extension_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t, {{refKind}} int x, {{refKind}} int y = 2) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + if (refKind == "ref readonly") + { + comp.VerifyEmitDiagnostics( + // (11,110): warning CS9200: A default value is specified for 'ref readonly' parameter 'y', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // public static void Add(this MyCollection collection, T t, ref readonly int x, ref readonly int y = 2) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "2").WithArguments("y").WithLocation(11, 110), + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + else if (refKind == "in") + { + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + else + { + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + } + + [Fact] + public void AddMethod_Extension_04() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, params T[] args) + { + if (args is null) return; + foreach (var a in args) + collection.__AddInternal(a); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = new() { (int?)null, null }; + MyCollection y = [(int?)null, null]; + x.Report(); + y.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[null], [null], "); + } + + [Fact] + public void AddMethod_Extension_05() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, params T[] y) + { + collection.__AddInternal(x); + foreach (var a in y) + collection.__AddInternal(a); + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_Extension_06() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, T y = default, params T[] z) + { + collection.__AddInternal(x); + collection.__AddInternal(y); + foreach (var a in z) + collection.__AddInternal(a); + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, null, 2, null, 3, null], "); + } + + [Fact] + public void AddMethod_Extension_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + public static class Extensions + { + public static void Add(this MyCollection collection, string s) { } + } + namespace N + { + internal static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + """; + var comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + + string sourceB = """ + using N; + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (8,32): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(8, 32), + // (8,32): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "string").WithLocation(8, 32), + // (8,35): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "string").WithLocation(8, 35), + // (8,37): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(8, 37)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref")] + [InlineData("ref readonly")] + public void AddMethod_Extension_08A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + struct MyCollection : IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + internal void __AddInternal(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + static class Extensions + { + public static void Add(this {{refKind}} MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + string expectedOutput = (refKind == "ref") ? "[1, 2, 3], " : "[], "; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: expectedOutput); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref")] + [InlineData("ref readonly")] + public void AddMethod_Extension_08B(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + internal void __AddInternal(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + static class Extensions + { + public static void Add(this {{refKind}} MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + switch (refKind) + { + case "": + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + break; + case "in": + case "ref readonly": + comp.VerifyEmitDiagnostics( + // (12,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'Add' must be a concrete (non-generic) value type. + // public static void Add(this in MyCollection collection, T t) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "Add").WithArguments("Add").WithLocation(12, 24), + // (20,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(20, 31)); + break; + case "ref": + comp.VerifyEmitDiagnostics( + // (12,24): error CS8337: The first parameter of a 'ref' extension method 'Add' must be a value type or a generic type constrained to struct. + // public static void Add(this ref MyCollection collection, T t) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, "Add").WithArguments("Add").WithLocation(12, 24), + // (20,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(20, 31)); + break; + } + } + + [Fact] + public void AddMethod_Extension_09() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // } + // public static class Extensions + // { + // public static void Add(this out MyCollection collection, T t) => throw null; + // } + string sourceA = """ + .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } + .assembly '<>' + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + } + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + } + .class public abstract sealed Extensions + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig static void Add([out] valuetype MyCollection`1& collection, !!T t) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + } + """; + var refA = CompileIL(sourceA, prependDefaultHeader: false); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 31)); + } + + [Fact] + public void AddMethod_Extension_10_WrongThisType() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this string collection, int x) => throw null; + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1929: 'MyCollection' does not contain a definition for 'Add' and the best extension method overload 'Extensions.Add(string, int)' requires a receiver of type 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "[x, ..y]").WithArguments("MyCollection", "Add", "Extensions.Add(string, int)", "string").WithLocation(19, 32), + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,32): error CS1929: 'MyCollection' does not contain a definition for 'Add' and the best extension method overload 'Extensions.Add(string, int)' requires a receiver of type 'string' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new() { x }").WithArguments("MyCollection", "Add", "Extensions.Add(string, int)", "string").WithLocation(20, 32), + // (20,40): error CS1950: The best overloaded Add method 'Extensions.Add(string, int)' for the collection initializer has some invalid arguments + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(string, int)").WithLocation(20, 40) + ); + } + + [Fact] + public void AddMethod_Extension_11_WrongThisType() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this IEnumerable collection, int x) => throw null; + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "x").WithArguments("MyCollection", "Add").WithLocation(20, 40) + ); + } + + [Fact] + public void AddMethod_Extension_12_WrongThisType() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (7,39): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 39)); + } + + [Fact] + public void AddMethod_Extension_13_WrongThisType() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this IEnumerable collection, T t) { ((MyCollection)collection).__AddInternal(t); } + } + """; + string sourceB = """ + class Program + { + static void Main() + { + string x = "1"; + string[] y = ["2", "3"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_Extension_14_ConstraintsViolated() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, params T[] args) + where T : struct + { + if (args is null) return; + foreach (var a in args) + collection.__AddInternal(a); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + MyCollection w = new() { x }; + } + } + """; + CreateCompilation([sourceA, sourceB1, s_collectionExtensions]).VerifyDiagnostics( + // (7,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 32), + // (9,40): error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Extensions.Add(MyCollection, params T[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("Extensions.Add(MyCollection, params T[])", "T", "int?").WithLocation(9, 40) + ); } [Fact] - public void TypeInference_LambdaReturn() + public void AddMethod_Extension_15_Dynamic() { - var source = """ - #nullable enable - using System; + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this dynamic d, T t) { d.__AddInternal(t); } + } + """; + string sourceB = """ class Program { static void Main() { - object x = new object(); - object y = null; - object[] z = new[] { x }; - F(z, () => [x]); - F(z, () => [y]); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; } - static void F(T t, Func f) { } } """; - var comp = CreateCompilation(source); + var comp = CreateCompilation([sourceA, sourceB]); comp.VerifyEmitDiagnostics( - // (8,20): warning CS8600: Converting null literal or possible null value to non-nullable type. - // object y = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 20), - // (11,21): warning CS8601: Possible null reference assignment. - // F(z, () => [y]); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(11, 21)); + // (11,36): error CS1103: The first parameter of an extension method cannot be of type 'dynamic' + // public static void Add(this dynamic d, T t) { d.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_BadTypeforThis, "dynamic").WithArguments("dynamic").WithLocation(11, 36)); } - [Fact] - public void TargetTypedElement_PublicAPI_List() + [WorkItem("https://github.com/dotnet/roslyn/issues/72769")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72769")] + public void AddMethod_RefOmittedArguments() { - var source = """ - using System.Collections.Generic; - class C + string sourceA = """ + using System; + using System.Collections; + using System.Runtime.InteropServices; + + [ComImport] + [Guid("5CDF1E39-B461-4A9B-9359-1D6F7DECE1B3")] + class MyCollection : IEnumerable + { + extern IEnumerator IEnumerable.GetEnumerator(); + } + + static class Extensions + { + public static void Add(this MyCollection collection, ref T x) => throw null; + } + """; + string sourceB = """ + class Program { static void Main() { - List items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation([sourceA, sourceB]); + // https://github.com/dotnet/roslyn/issues/72769: VerifyEmitDiagnostics() results in Debug.Assert + // failures in LocalRewriter.MakeCollectionInitializer() and GetEffectiveArgumentRefKinds(). + comp.VerifyEmitDiagnostics(); } [Fact] - public void TargetTypedElement_PublicAPI_Array() + public void AddMethod_Base() { - var source = """ - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + protected abstract void __AddInternal(T t); + public void Add(T t) => __AddInternal(t); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + protected override void __AddInternal(T t) { _list.Add(t); } + } + class Program { static void Main() { - object[] items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } [Fact] - public void TargetTypedElement_PublicAPI_Span() + public void AddMethod_Derived() { - var source = """ - using System; - - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + protected void __AddInternal(T t) { _list.Add(t); } + } + class MyCollectionDerived : MyCollection + { + public void Add(T t) => __AddInternal(t); + } + class Program { static void Main() { - Span items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollectionDerived z = [x, ..y]; + z.Report(); } } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); + [Theory] + [InlineData("class")] + [InlineData("struct")] + public void AddMethod_ExplicitImplementation(string structOrClass) + { + string sourceA = $$""" + using System.Collections; + using System.Collections.Generic; + interface IAdd + { + void Add(T t); + } + {{structOrClass}} MyCollection : IAdd, IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + void IAdd.Add(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + """; + string sourceB = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + string sourceC = $$""" + static class Extensions + { + public static void Add(this {{(structOrClass == "struct" ? "ref" : "")}} T collection, U u) + where T : {{structOrClass}}, IAdd + { + collection.Add(u); + } + } + """; - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 34)); - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } [Fact] - public void TargetTypedElement_PublicAPI_ReadOnlySpan() + public void AddMethod_Static() { - var source = """ - using System; - - class C + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public static void Add(T t) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + """; + string sourceB = """ + class Program { static void Main() { - ReadOnlySpan items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1921: The best overloaded method match for 'MyCollection.Add(object)' has wrong signature for the initializer element. The initializable Add must be an accessible instance method. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_InitializerAddHasWrongSignature, "[x, ..y]").WithArguments("MyCollection.Add(object)").WithLocation(7, 34)); - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.ReadOnlySpan) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + [Fact] + public void AddMethod_Generic_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U u) { _list.Add(u is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection z = [null]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'MyCollection.Add(U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("MyCollection.Add(U)").WithLocation(5, 35)); } [Fact] - public void TargetTypedElement_PublicAPI_ImmutableArray() + public void AddMethod_Generic_02() { - var source = """ - using System.Collections.Immutable; - - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } + } + class Program { static void Main() { - ImmutableArray items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - 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()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 34)); } [Fact] - public void TargetTypedElement_PublicAPI_IEnumerableT() + public void AddMethod_Generic_03() { - var source = """ + string source = """ + using System.Collections; using System.Collections.Generic; - - class C + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, U u = default) { _list.Add(t); } + } + class Program { static void Main() { - IEnumerable items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + comp.VerifyEmitDiagnostics( + // (15,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 34)); } [Fact] - public void TargetTypedElement_PublicAPI_ImplementsIEnumerable() + public void AddMethod_Generic_04() { - var source = """ + string sourceA = """ using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, U u) { collection.__AddInternal(u is T t ? t : default); } + } + """; - class C + string sourceB1 = """ + class Program { static void Main() { - MyCollection items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); - class MyCollection : IEnumerable + string sourceB2 = """ + class Program { - IEnumerator IEnumerable.GetEnumerator() => throw null!; - public void Add(object obj) { } + static void Main() + { + MyCollection z = [null]; + } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[new()]') - 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) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, U)").WithLocation(5, 35)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_01() + public void AddMethod_Generic_05() { string source = """ - using System; + using System.Collections; using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } static class Extensions { - public static void Add(this ICollection collection, params T[] elements) - { - foreach (T element in elements) - collection.Add(element); - } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } class Program { - static Dictionary CreateDictionary(ICollection> collection) - { - return /**/[..collection]/**/; - } static void Main() { - var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; - var d = CreateDictionary(v); - foreach (var kvp in d) - Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), "); - - verifier.VerifyIL("Extensions.Add(this System.Collections.Generic.ICollection, params T[])", """ - { - // Code size 32 (0x20) - .maxstack 2 - .locals init (T[] V_0, - int V_1, - T V_2) //element - IL_0000: ldarg.1 - IL_0001: stloc.0 - IL_0002: ldc.i4.0 - IL_0003: stloc.1 - IL_0004: br.s IL_0019 - IL_0006: ldloc.0 - IL_0007: ldloc.1 - IL_0008: ldelem "T" - IL_000d: stloc.2 - IL_000e: ldarg.0 - IL_000f: ldloc.2 - IL_0010: callvirt "void System.Collections.Generic.ICollection.Add(T)" - IL_0015: ldloc.1 - IL_0016: ldc.i4.1 - IL_0017: add - IL_0018: stloc.1 - IL_0019: ldloc.1 - IL_001a: ldloc.0 - IL_001b: ldlen - IL_001c: conv.i4 - IL_001d: blt.s IL_0006 - IL_001f: ret - } - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') - Elements(1): - ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') - Operand: - IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 34)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsCollection_01() + public void AddMethod_Generic_06() { - string source = """ - using System; + string sourceA = """ + using System.Collections; using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } static class Extensions { - public static void Add(this ICollection collection, params IEnumerable elements) - { - foreach (T element in elements) - collection.Add(element); - } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } + """; + + string sourceB1 = """ class Program { - static Dictionary CreateDictionary(ICollection> collection) + static void Main() { - return /**/[..collection]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { static void Main() { - var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; - var d = CreateDictionary(v); - foreach (var kvp in d) - Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + MyCollection z = [null]; } } """; - - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), ").VerifyDiagnostics(); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') - Elements(1): - ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') - Operand: - IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, T)").WithLocation(5, 35)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_02() + public void AddMethod_Generic_07() { string source = """ using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params T[] x) => _list.AddRange(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, U y = default, T z = default) { collection.__AddInternal(x is U u ? u : default); } } class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_03() + public void AddMethod_Generic_08() { - string source = """ + string sourceA = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal void __AddRange(T[] x) { _list.AddRange(x); } + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(Func f) { _list.Add(f); } } - static class Extensions + """; + + string sourceB1 = """ + class Program { - public static void Add(this MyCollection c, params T[] x) { c.__AddRange(x); } + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } } + """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "x").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 35), + // (7,40): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "y").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 40), + // (7,43): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 43)); + + string sourceB2 = """ + using System; class Program { static void Main() { - int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + Func x = _ => 1; + Func[] y = [_ => "2"]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[System.Func`2[System.Object,System.Int32], System.Func`2[System.Object,System.String]], "); } [Fact] - public void Add_ParamsArray_04() + public void AddMethod_Generic_09() { - string source = """ + string sourceA = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(T x, params T[] y) => _list.Add(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(Delegate d) { _list.Add(d); } } + static class Extensions + { + public static void Add(this MyCollection collection, Func f) { collection.__AddInternal(f); } + } + """; + + string sourceB1 = """ class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "x").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 35), + // (7,40): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "y").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 40), + // (7,43): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 43)); - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + string sourceB2 = """ + using System; + class Program + { + static void Main() + { + Func x = _ => 1; + Func[] y = [_ => "2"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[System.Func`2[System.Object,System.Int32], System.Func`2[System.Object,System.String]], "); } + // [Obsolete] attribute is ignored when checking for Add for conversion. [Fact] - public void Add_ParamsArray_05() + public void AddMethod_Obsolete_01() { string source = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal void __Add(T x) { _list.Add(x); } + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + [Obsolete("do not use", error: true)] + public void Add(string s) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } } static class Extensions { - public static void Add(this MyCollection c, T x, params T[] y) { c.__Add(x); } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + // [Obsolete] attribute is ignored when checking for Add for conversion. [Fact] - public void Add_ParamsArray_06() + public void AddMethod_Obsolete_02() { - string sourceA = """ + string source = """ + using N; using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params MyCollection[] x) + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + [Obsolete("do not use", error: true)] + public static void Add(this MyCollection collection, string s) => throw null; + } + namespace N + { + static class Extensions { - Console.Write("Add: "); - x.Report(); - Console.WriteLine(); - _list.AddRange(x); + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - """; - - string sourceB1 = """ class Program { static void Main() { - MyCollection x = []; - MyCollection[] y = []; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: [[]], - [[]], - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') - ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); - - string sourceB2 = """ + [Fact] + public void AddMethod_Unsafe_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + unsafe public void Add(void* p) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + """; + string sourceB = """ class Program { static void Main() { - MyCollection x = /**/[[]]/**/; - x.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; - - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: [], - [], - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection[], IsImplicit) (Syntax: '[]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection[]) (Syntax: '[]') - Elements(0) - """); + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; + + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'MyCollection.Add(void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(void*)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 1: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "void*").WithLocation(7, 35), + // (7,38): error CS1503: Argument 1: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "void*").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'MyCollection.Add(void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(void*)").WithLocation(7, 40)); + + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe, expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_07() + public void AddMethod_Unsafe_02() { string sourceA = """ using System.Collections; using System.Collections.Generic; - struct MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list; - public void Add(params MyCollection?[] x) => GetList().AddRange(x); - public IEnumerator GetEnumerator() => GetList().GetEnumerator(); + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private List GetList() => _list ??= new(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions1 + { + unsafe public static void Add(this MyCollection collection, void* p) => throw null; } """; @@ -34134,216 +37735,238 @@ class Program { static void Main() { - MyCollection x = []; - MyCollection[] y = []; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; } } """; - - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[[]], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') - ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - """); + var comp = CreateCompilation([sourceA, sourceB1, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'Extensions1.Add(MyCollection, void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions1.Add(MyCollection, void*)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 2: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "void*").WithLocation(7, 35), + // (7,38): error CS1503: Argument 2: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "void*").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'Extensions1.Add(MyCollection, void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions1.Add(MyCollection, void*)").WithLocation(7, 40)); string sourceB2 = """ + using N; class Program { static void Main() { - MyCollection? x = /**/[[]]/**/; - x.Value.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + namespace N + { + static class Extensions2 + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } } """; - - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?[], IsImplicit) (Syntax: '[]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection?[]) (Syntax: '[]') - Elements(0) - """); + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe, expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_08() + public void AddMethod_RefStruct_01() { - string sourceA = """ + string source = """ using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + ref struct R { } + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params object[] x) + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(R r) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() { - Console.Write("Add: "); - foreach (var i in x) - Console.Write("{0}, ", i); - Console.WriteLine(); - _list.AddRange(x); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - string sourceB1 = """ + [Fact] + public void AddMethod_RefStruct_02() + { + string source = """ + using System; + using System.Collections; + using System.Collections.Generic; + ref struct R + { + public R(object value) { Value = value; } + public readonly object Value; + } + class MyCollection : IEnumerable + { + private List _list = new(); + public MyEnumerator GetEnumerator() => new MyEnumerator(_list); + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object o) => throw null; + internal void __AddInternal(R r) { _list.Add(r.Value); } + } + class MyEnumerator + { + private List _list; + private int _index = -1; + public MyEnumerator(List list) { _list = list; } + public bool MoveNext() + { + if (_index < _list.Count) _index++; + return _index < _list.Count; + } + public R Current => new R(_list[_index]); + } + static class Extensions + { + public static void Add(this MyCollection collection, R r) { collection.__AddInternal(r); } + } class Program { static void Main() { - object x = 1; - object[] y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + MyCollection x = [new R(1)]; + MyCollection y = [..x, new R(2)]; + foreach (var i in y) + Console.Write("{0}, ", i.Value); } } """; + CompileAndVerify(source, verify: Verification.FailsILVerify, expectedOutput: "1, 2, "); + } - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: 1, - Add: 2, - Add: 3, - [1, 2, 3], - """); + [Fact] + public void AddMethod_UseSiteErrors() + { + string assemblyA = GetUniqueName(); + string sourceA = """ + public class A { } + """; + var comp = CreateCompilation(sourceA, assemblyName: assemblyA); + var refA = comp.EmitToImageReference(); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + string sourceB = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(A a) => throw null; + public void __AddInternal(T t) { _list.Add(t); } + } + """; + comp = CreateCompilation(sourceB, references: [refA]); + var refB = comp.EmitToImageReference(); - string sourceB2 = """ + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } class Program { static void Main() { - object[] x = [1]; - object[][] y = [[2, 3]]; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: 1, - Add: 2, 3, - [1, 2, 3], - """); + CompileAndVerify([sourceC, s_collectionExtensions], references: [refA, refB], expectedOutput: "[1, 2, 3], "); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object[]) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[][]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refB]); + comp.VerifyEmitDiagnostics( + // (11,35): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '2537f385-b53e-4fea-834a-b23059cd7f17, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "x").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(11, 35), + // (11,40): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '2537f385-b53e-4fea-834a-b23059cd7f17, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "y").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(11, 40)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_09() + public void AddMethod_UseSiteErrors_ParamCollection() { - string sourceA1 = """ - public abstract class MyCollectionBase + string assemblyA = GetUniqueName(); + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollectionA : IEnumerable { - public abstract void Add(object[] x); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } } """; - string assemblyName = GetUniqueName(); - var comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(1, 0, 0, 0)), sourceA1, references: TargetFrameworkUtil.StandardReferences); - var refA1 = comp.EmitToImageReference(); + var comp = CreateCompilation(sourceA, assemblyName: assemblyA); + var refA = comp.EmitToImageReference(); string sourceB = """ using System.Collections; using System.Collections.Generic; - public class MyCollection : MyCollectionBase, IEnumerable + public class MyCollectionB : IEnumerable { - private List _list = new(); - public override void Add(object[] x) => _list.AddRange(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U x, params MyCollectionA y) where U : T { _list.Add(x); _list.AddRange(y); } } """; - comp = CreateCompilation(sourceB, references: [refA1]); + comp = CreateCompilation(sourceB, references: [refA]); var refB = comp.EmitToImageReference(); - string sourceA2 = """ - public abstract class MyCollectionBase - { - public abstract void Add(params object[] x); - } - """; - comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(2, 0, 0, 0)), sourceA2, references: TargetFrameworkUtil.StandardReferences); - var refA2 = comp.EmitToImageReference(); - string sourceC = """ class Program { static void Main() { - object x = 1; - object[] y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollectionB z = [x, ..y]; z.Report(); } } """; - comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refA2, refB], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); + CompileAndVerify([sourceC, s_collectionExtensions], references: [refA, refB], expectedOutput: "[1, 2, 3], "); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refB]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0012: The type 'MyCollectionA<>' is defined in an assembly that is not referenced. You must add a reference to assembly '41f5b758-1e64-4c10-88d8-6dd8029c374c, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollectionB z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[x, ..y]").WithArguments("MyCollectionA<>", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 35)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index d49e01cfcdde5..83bd9f2da8660 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -14926,5 +14926,164 @@ static void M2(params IEnumerable x) Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C1").WithLocation(16, 17) ); } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Element { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + Test(new Element(), null); + } + + static void Test(params ElementCollection c) + { + c.Report(); + } + } + """; + CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Base { } + class Element : Base { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + Test(new Element(), null); + } + + static void Test(params ElementCollection c) + { + c.Report(); + } + } + """; + CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/71240")] + [Fact] + public void AddMethod_Derived_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + + class Sample : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { if (t is object[] o) _list.Add(o); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + Test(["a"], ["b"], ["c"]); + } + + static void Test(params Sample s) + { + s.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[[a], [b], [c]], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + Test("a", null); + } + + static void Test(params Sample s) + { + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,14): error CS1503: Argument 1: cannot convert from 'string' to 'object[]' + // Test("a", null); + Diagnostic(ErrorCode.ERR_BadArgType, @"""a""").WithArguments("1", "string", "object[]").WithLocation(5, 14) + ); + } + + [Fact] + public void AddMethod_Generic_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } + } + class Program + { + + static void Test(params MyCollection z) + { + } + + static void Main() + { + int x = 1; + Test(x); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,22): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // static void Test(params MyCollection z) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "params MyCollection z").WithArguments("MyCollection").WithLocation(12, 22), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(x); + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14) + ); + } } }