diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index a47b97af47889..a1910dcafe0aa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -484,6 +484,8 @@ internal virtual LocalSymbol? LocalInProgress } } + internal virtual NamedTypeSymbol? ParamsCollectionTypeInProgress => null; + internal virtual BoundExpression? ConditionalReceiverExpression { get diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index 0d8d9d431f784..267c5d7e9c05f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -911,7 +911,7 @@ private TypedConstant VisitConversion(BoundConversion node, BindingDiagnosticBag var operandType = operand.Type; if (node.Conversion.IsCollectionExpression - && node.Conversion.GetCollectionExpressionTypeKind(out _) == CollectionExpressionTypeKind.Array) + && node.Conversion.GetCollectionExpressionTypeKind(out _, out _, out _) == CollectionExpressionTypeKind.Array) { Debug.Assert(type.IsSZArray()); return VisitArrayCollectionExpression(type, (BoundCollectionExpression)operand, diagnostics, ref attrHasErrors, curArgumentHasErrors); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index a3b37b965f367..bf78efd1c1f8c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -564,7 +564,7 @@ private BoundCollectionExpression ConvertCollectionExpression( _ = GetSpecialTypeMember(SpecialMember.System_Nullable_T__ctor, diagnostics, syntax: node.Syntax); } - var collectionTypeKind = conversion.GetCollectionExpressionTypeKind(out var elementType); + var collectionTypeKind = conversion.GetCollectionExpressionTypeKind(out var elementType, out MethodSymbol? constructor, out bool isExpanded); if (collectionTypeKind == CollectionExpressionTypeKind.None) { @@ -600,40 +600,16 @@ private BoundCollectionExpression ConvertCollectionExpression( Debug.Assert(elementType is { }); var namedType = (NamedTypeSymbol)targetType; - bool result = namedType.HasCollectionBuilderAttribute(out TypeSymbol? builderType, out string? methodName); - Debug.Assert(result); - var targetTypeOriginalDefinition = targetType.OriginalDefinition; - result = TryGetCollectionIterationType(syntax, targetTypeOriginalDefinition, out TypeWithAnnotations elementTypeOriginalDefinition); - Debug.Assert(result); - - var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - Conversion collectionBuilderReturnTypeConversion; - collectionBuilderMethod = GetCollectionBuilderMethod(namedType, elementTypeOriginalDefinition.Type, builderType, methodName, ref useSiteInfo, out collectionBuilderReturnTypeConversion); - diagnostics.Add(syntax, useSiteInfo); + collectionBuilderMethod = GetAndValidateCollectionBuilderMethod(syntax, namedType, diagnostics, out var updatedElementType); if (collectionBuilderMethod is null) { - diagnostics.Add(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, syntax, methodName ?? "", elementTypeOriginalDefinition, targetTypeOriginalDefinition); return BindCollectionExpressionForErrorRecovery(node, targetType, inConversion: true, diagnostics); } - Debug.Assert(collectionBuilderReturnTypeConversion.Exists); + elementType = updatedElementType; collectionBuilderInvocationPlaceholder = new BoundValuePlaceholder(syntax, collectionBuilderMethod.ReturnType) { WasCompilerGenerated = true }; collectionBuilderInvocationConversion = CreateConversion(collectionBuilderInvocationPlaceholder, targetType, diagnostics); - - ReportUseSite(collectionBuilderMethod, diagnostics, syntax.Location); - - var parameterType = (NamedTypeSymbol)collectionBuilderMethod.Parameters[0].Type; - Debug.Assert(parameterType.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)); - - elementType = parameterType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; - - collectionBuilderMethod.CheckConstraints( - new ConstraintsHelper.CheckConstraintsArgs(Compilation, Conversions, syntax.Location, diagnostics)); - - ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod.ContainingType, syntax, hasBaseReceiver: false); - ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod, syntax, hasBaseReceiver: false); - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, collectionBuilderMethod, syntax, isDelegateConversion: false); } break; @@ -655,6 +631,8 @@ private BoundCollectionExpression ConvertCollectionExpression( { implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, diagnostics); + Debug.Assert((collectionCreation is BoundNewT && !isExpanded && constructor is null) || + (collectionCreation is BoundObjectCreationExpression creation && creation.Expanded == isExpanded && creation.Constructor == constructor)); var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod); foreach (var element in elements) @@ -758,8 +736,59 @@ BoundNode bindSpreadElement(BoundCollectionExpressionSpreadElement element, Type } } + internal MethodSymbol? GetAndValidateCollectionBuilderMethod( + SyntaxNode syntax, + NamedTypeSymbol namedType, + BindingDiagnosticBag diagnostics, + out TypeSymbol? elementType) + { + MethodSymbol? collectionBuilderMethod; + bool result = namedType.HasCollectionBuilderAttribute(out TypeSymbol? builderType, out string? methodName); + Debug.Assert(result); + + var targetTypeOriginalDefinition = namedType.OriginalDefinition; + result = TryGetCollectionIterationType(syntax, targetTypeOriginalDefinition, out TypeWithAnnotations elementTypeOriginalDefinition); + Debug.Assert(result); + + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + Conversion collectionBuilderReturnTypeConversion; + collectionBuilderMethod = GetCollectionBuilderMethod(namedType, elementTypeOriginalDefinition.Type, builderType, methodName, ref useSiteInfo, out collectionBuilderReturnTypeConversion); + diagnostics.Add(syntax, useSiteInfo); + if (collectionBuilderMethod is null) + { + diagnostics.Add(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, syntax, methodName ?? "", elementTypeOriginalDefinition, targetTypeOriginalDefinition); + elementType = null; + return null; + } + + Debug.Assert(collectionBuilderReturnTypeConversion.Exists); + + ReportUseSite(collectionBuilderMethod, diagnostics, syntax.Location); + + var parameterType = (NamedTypeSymbol)collectionBuilderMethod.Parameters[0].Type; + Debug.Assert(parameterType.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)); + + elementType = parameterType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; + + collectionBuilderMethod.CheckConstraints( + new ConstraintsHelper.CheckConstraintsArgs(Compilation, Conversions, syntax.Location, diagnostics)); + + ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod.ContainingType, syntax, hasBaseReceiver: false); + ReportDiagnosticsIfObsolete(diagnostics, collectionBuilderMethod, syntax, hasBaseReceiver: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, collectionBuilderMethod, syntax, isDelegateConversion: false); + + return collectionBuilderMethod; + } + internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax, TypeSymbol targetType, BindingDiagnosticBag diagnostics) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableConstructor check + // this function should be kept in sync with HasCollectionExpressionApplicableConstructor. + // + BoundExpression collectionCreation; var analyzedArguments = AnalyzedArguments.GetInstance(); if (targetType is NamedTypeSymbol namedType) @@ -781,25 +810,454 @@ internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax, return collectionCreation; } - internal bool HasCollectionExpressionApplicableConstructor(SyntaxNode syntax, TypeSymbol targetType, BindingDiagnosticBag diagnostics) + internal bool HasCollectionExpressionApplicableConstructor(SyntaxNode syntax, TypeSymbol targetType, out MethodSymbol? constructor, out bool isExpanded, BindingDiagnosticBag diagnostics) + { + // This is what BindClassCreationExpression is doing in terms of reporting diagnostics + + constructor = null; + isExpanded = false; + + if (targetType is NamedTypeSymbol namedType) + { + // This is what BindClassCreationExpression called by BindCollectionExpressionConstructor is doing in terms of reporting diagnostics + + if (namedType.IsAbstract) + { + // Report error for new of abstract type. + diagnostics.Add(ErrorCode.ERR_NoNewAbstract, syntax.Location, namedType); + return false; + } + + if (HasParamsCollectionTypeInProgress(namedType)) + { + // We are in a cycle. Optimistically assume we have the right constructor to break the cycle + return true; + } + + var analyzedArguments = AnalyzedArguments.GetInstance(); + var binder = new ParamsCollectionTypeInProgressBinder(namedType, this); + + bool overloadResolutionSucceeded = binder.TryPerformConstructorOverloadResolution( + namedType, + analyzedArguments, + namedType.Name, + syntax.Location, + suppressResultDiagnostics: false, + diagnostics, + out MemberResolutionResult memberResolutionResult, + candidateConstructors: out _, + allowProtectedConstructorsOfBaseType: false, + out CompoundUseSiteInfo overloadResolutionUseSiteInfo); + + analyzedArguments.Free(); + + if (overloadResolutionSucceeded) + { + bindClassCreationExpressionContinued(binder, syntax, memberResolutionResult, in overloadResolutionUseSiteInfo, diagnostics); + constructor = memberResolutionResult.Member; + isExpanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; + } + else + { + reportAdditionalDiagnosticsForOverloadResolutionFailure(syntax, in overloadResolutionUseSiteInfo, diagnostics); + } + + return overloadResolutionSucceeded; + } + else if (targetType is TypeParameterSymbol typeParameter) + { + return TypeParameterHasParameterlessConstructor(syntax, typeParameter, diagnostics); + } + else + { + throw ExceptionUtilities.UnexpectedValue(targetType); + } + + // This is what BindClassCreationExpressionContinued is doing in terms of reporting diagnostics + static void bindClassCreationExpressionContinued( + Binder binder, + SyntaxNode node, + MemberResolutionResult memberResolutionResult, + in CompoundUseSiteInfo overloadResolutionUseSiteInfo, + BindingDiagnosticBag diagnostics) + { + ReportConstructorUseSiteDiagnostics(node.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); + + var method = memberResolutionResult.Member; + + binder.ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false); + // NOTE: Use-site diagnostics were reported during overload resolution. + + CheckRequiredMembersInObjectInitializer(method, initializers: default, node, diagnostics); + } + + // This is what CreateBadClassCreationExpression is doing in terms of reporting diagnostics + static void reportAdditionalDiagnosticsForOverloadResolutionFailure( + SyntaxNode typeNode, + in CompoundUseSiteInfo overloadResolutionUseSiteInfo, + BindingDiagnosticBag diagnostics) + { + ReportConstructorUseSiteDiagnostics(typeNode.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); + } + } + + private bool HasParamsCollectionTypeInProgress(NamedTypeSymbol toCheck) { - var collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, diagnostics); - return !collectionCreation.HasErrors; + Binder? current = this; + while (current?.Flags.Includes(BinderFlags.CollectionExpressionConversionValidation) == true) + { + if (current.ParamsCollectionTypeInProgress?.OriginalDefinition.Equals(toCheck.OriginalDefinition, TypeCompareKind.AllIgnoreOptions) == true) + { + // We are in a cycle. + return true; + } + + current = current.Next; + } + + return false; } - internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, TypeSymbol elementType, BindingDiagnosticBag diagnostics) + internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, TypeSymbol elementType, out ImmutableArray addMethods, BindingDiagnosticBag diagnostics) { + Debug.Assert(!targetType.IsDynamic()); + + NamedTypeSymbol? namedType = targetType as NamedTypeSymbol; + + if (namedType is not null && HasParamsCollectionTypeInProgress(namedType)) + { + // We are in a cycle. Optimistically assume we have the right Add to break the cycle + addMethods = []; + return true; + } + var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; var elementPlaceholder = new BoundValuePlaceholder(syntax, elementType) { WasCompilerGenerated = true }; var addMethodBinder = WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod | BinderFlags.CollectionExpressionConversionValidation); - var result = BindCollectionInitializerElementAddMethod( - syntax, - ImmutableArray.Create(elementPlaceholder), - hasEnumerableInitializerType: true, + + if (namedType is not null) + { + addMethodBinder = new ParamsCollectionTypeInProgressBinder(namedType, addMethodBinder); + } + + return bindCollectionInitializerElementAddMethod( addMethodBinder, + syntax, + elementPlaceholder, diagnostics, - implicitReceiver); - return !result.HasErrors; + implicitReceiver, + out addMethods); + + // This is what BindCollectionInitializerElementAddMethod is doing in terms of reporting diagnostics and detecting a failure + static bool bindCollectionInitializerElementAddMethod( + Binder addMethodBinder, + SyntaxNode elementInitializer, + BoundValuePlaceholder arg, + BindingDiagnosticBag diagnostics, + BoundObjectOrCollectionValuePlaceholder implicitReceiver, + out ImmutableArray addMethods) + { + return makeInvocationExpression( + addMethodBinder, + elementInitializer, + implicitReceiver, + arg: arg, + diagnostics, + out addMethods); + } + + // This is what MakeInvocationExpression is doing in terms of reporting diagnostics and detecting a failure + static bool makeInvocationExpression( + Binder addMethodBinder, + SyntaxNode node, + BoundExpression receiver, + BoundValuePlaceholder arg, + BindingDiagnosticBag diagnostics, + out ImmutableArray addMethods) + { + var boundExpression = addMethodBinder.BindInstanceMemberAccess( + node, node, receiver, WellKnownMemberNames.CollectionInitializerAddMethodName, rightArity: 0, + typeArgumentsSyntax: default(SeparatedSyntaxList), + typeArgumentsWithAnnotations: default(ImmutableArray), + invoked: true, indexed: false, diagnostics, searchExtensionMethodsIfNecessary: true); + + // require the target member to be a method. + if (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess) + { + ReportMakeInvocationExpressionBadMemberKind(node, WellKnownMemberNames.CollectionInitializerAddMethodName, boundExpression, diagnostics); + addMethods = []; + return false; + } + + if (boundExpression.Kind != BoundKind.MethodGroup) + { + Debug.Assert(boundExpression.HasErrors); + addMethods = []; + return false; + } + + var analyzedArguments = AnalyzedArguments.GetInstance(); + analyzedArguments.Arguments.AddRange(arg); + + bool result = bindInvocationExpression( + addMethodBinder, node, node, (BoundMethodGroup)boundExpression, analyzedArguments, diagnostics, out addMethods); + + analyzedArguments.Free(); + return result; + } + + // This is what BindInvocationExpression is doing in terms of reporting diagnostics and detecting a failure + static bool bindInvocationExpression( + Binder addMethodBinder, + SyntaxNode node, + SyntaxNode expression, + BoundMethodGroup boundExpression, + AnalyzedArguments analyzedArguments, + BindingDiagnosticBag diagnostics, + out ImmutableArray addMethods) + { + return bindMethodGroupInvocation( + addMethodBinder, node, expression, boundExpression, analyzedArguments, + 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, + SyntaxNode syntax, + SyntaxNode expression, + BoundMethodGroup methodGroup, + AnalyzedArguments analyzedArguments, + BindingDiagnosticBag diagnostics, + out ImmutableArray addMethods) + { + 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)); + + diagnostics.Add(expression, useSiteInfo); + + if (!methodGroup.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading. + + if (resolution.HasAnyErrors) + { + addMethods = []; + result = false; + } + else if (!resolution.IsEmpty) + { + // We're checking resolution.ResultKind, rather than methodGroup.HasErrors + // to better handle the case where there's a problem with the receiver + // (e.g. inaccessible), but the method group resolved correctly (e.g. because + // it's actually an accessible static method on a base type). + // CONSIDER: could check for error types amongst method group type arguments. + if (resolution.ResultKind != LookupResultKind.Viable) + { + addMethods = []; + result = false; + } + else + { + // 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) + { + // 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 + // that are not performed at compile-time. + // Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime. + var finalApplicableCandidates = addMethodBinder.GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult, + methodGroup.ReceiverOpt, + methodGroup.TypeArgumentsOpt, + invokedAsExtensionMethod: resolution.IsExtensionMethodGroup, + diagnostics); + + Debug.Assert(finalApplicableCandidates.Length != 1 || finalApplicableCandidates[0].IsApplicable); + + if (finalApplicableCandidates.Length == 0) + { + addMethods = []; + result = false; + } + else if (finalApplicableCandidates.Length == 1 && + tryEarlyBindSingleCandidateInvocationWithDynamicArgument(addMethodBinder, syntax, expression, methodGroup, diagnostics, resolution, finalApplicableCandidates[0], out var addMethod) is bool earlyBoundResult) + { + addMethods = addMethod is null ? [] : [addMethod]; + result = earlyBoundResult; + } + else + { + 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 = finalApplicableCandidates.SelectAsArray(r => r.Member); + result = bindDynamicInvocation(addMethodBinder, syntax, resolution.AnalyzedArguments, diagnostics); + } + } + } + else + { + result = bindInvocationExpressionContinued( + addMethodBinder, syntax, expression, resolution.OverloadResolutionResult, resolution.AnalyzedArguments, + resolution.MethodGroup, diagnostics: diagnostics, out var addMethod); + addMethods = addMethod is null ? [] : [addMethod]; + } + } + } + else + { + addMethods = []; + result = false; + } + + resolution.Free(); + return result; + } + + // This is what TryEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure + static bool? tryEarlyBindSingleCandidateInvocationWithDynamicArgument( + Binder addMethodBinder, + SyntaxNode syntax, + SyntaxNode expression, + BoundMethodGroup boundMethodGroup, + BindingDiagnosticBag diagnostics, + MethodGroupResolution resolution, + MemberResolutionResult methodResolutionResult, + out MethodSymbol? addMethod) + { + MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember; + if (!addMethodBinder.CanEarlyBindSingleCandidateInvocationWithDynamicArgument(syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) + { + addMethod = null; + return null; + } + + var resultWithSingleCandidate = OverloadResolutionResult.GetInstance(); + resultWithSingleCandidate.ResultsBuilder.Add(methodResolutionResult); + + // PROTOTYPE(ParamsCollections): This code path is affected by https://github.com/dotnet/roslyn/issues/71399 + bool result = bindInvocationExpressionContinued( + addMethodBinder, + node: syntax, + expression: expression, + result: resultWithSingleCandidate, + analyzedArguments: resolution.AnalyzedArguments, + methodGroup: resolution.MethodGroup, + diagnostics: diagnostics, + out addMethod); + + resultWithSingleCandidate.Free(); + + return result; + } + + // This is what BindInvocationExpressionContinued is doing in terms of reporting diagnostics and detecting a failure + static bool bindInvocationExpressionContinued( + Binder addMethodBinder, + SyntaxNode node, + SyntaxNode expression, + OverloadResolutionResult result, + AnalyzedArguments analyzedArguments, + MethodGroup methodGroup, + BindingDiagnosticBag diagnostics, + out MethodSymbol? addMethod) + { + Debug.Assert(node != null); + Debug.Assert(methodGroup != null); + Debug.Assert(methodGroup.Error == null); + Debug.Assert(methodGroup.Methods.Count > 0); + + var invokedAsExtensionMethod = methodGroup.IsExtensionMethodGroup; + + // We have already determined that we are not in a situation where we can successfully do + // a dynamic binding. We might be in one of the following situations: + // + // * There were dynamic arguments but overload resolution still found zero applicable candidates. + // * There were no dynamic arguments and overload resolution found zero applicable candidates. + // * There were no dynamic arguments and overload resolution found multiple applicable candidates + // without being able to find the best one. + // + // In those three situations we might give an additional error. + + if (!result.Succeeded) + { + // Since there were no argument errors to report, we report an error on the invocation itself. + result.ReportDiagnostics( + binder: addMethodBinder, location: GetLocationForOverloadResolutionDiagnostic(node, expression), nodeOpt: node, diagnostics: diagnostics, name: WellKnownMemberNames.CollectionInitializerAddMethodName, + receiver: methodGroup.Receiver, invokedExpression: expression, arguments: analyzedArguments, memberGroup: methodGroup.Methods.ToImmutable(), + typeContainingConstructor: null, delegateTypeBeingInvoked: null, queryClause: null); + + addMethod = null; + return false; + } + + // Otherwise, there were no dynamic arguments and overload resolution found a unique best candidate. + // We still have to determine if it passes final validation. + + var methodResult = result.ValidResult; + var method = methodResult.Member; + + // It is possible that overload resolution succeeded, but we have chosen an + // instance method and we're in a static method. A careful reading of the + // overload resolution spec shows that the "final validation" stage allows an + // "implicit this" on any method call, not just method calls from inside + // instance methods. Therefore we must detect this scenario here, rather than in + // overload resolution. + + var receiver = methodGroup.Receiver; + + // 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. + // Note: final validation wants the receiver that corresponds to the source representation + // (i.e. the first argument, if invokedAsExtensionMethod). + var gotError = addMethodBinder.MemberGroupFinalValidation(receiver, method, expression, diagnostics, invokedAsExtensionMethod); + + addMethodBinder.ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false); + + // No use site errors, but there could be use site warnings. + // If there are any use site warnings, they have already been reported by overload resolution. + Debug.Assert(!method.HasUseSiteError, "Shouldn't have reached this point if there were use site errors."); + Debug.Assert(!method.IsRuntimeFinalizer()); + + addMethod = method; + return !gotError; + } } /// @@ -909,13 +1367,13 @@ private void GenerateImplicitConversionErrorForCollectionExpression( if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable) { - if (!HasCollectionExpressionApplicableConstructor(node.Syntax, targetType, diagnostics)) + if (!HasCollectionExpressionApplicableConstructor(node.Syntax, targetType, constructor: out _, isExpanded: out _, diagnostics)) { reportedErrors = true; } if (elements.Length > 0 && - !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, elementType, diagnostics)) + !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, elementType, 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 25c6a23c0067c..3a0801bdc202a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -5889,6 +5889,14 @@ private BoundExpression BindCollectionInitializerElementAddMethod( BindingDiagnosticBag diagnostics, BoundObjectOrCollectionValuePlaceholder implicitReceiver) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.bindCollectionInitializerElementAddMethod + // + // SPEC: For each specified element in order, the collection initializer invokes an Add method on the target object // SPEC: with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation. // SPEC: Thus, the collection object must contain an applicable Add method for each element initializer. @@ -5906,77 +5914,104 @@ private BoundExpression BindCollectionInitializerElementAddMethod( return BadExpression(elementInitializer, LookupResultKind.NotInvocable, ImmutableArray.Empty, boundElementInitializerExpressions); } - Debug.Assert(collectionInitializerAddMethodBinder != null); - Debug.Assert(collectionInitializerAddMethodBinder.Flags.Includes(BinderFlags.CollectionInitializerAddMethod)); - Debug.Assert(implicitReceiver != null); - Debug.Assert((object)implicitReceiver.Type != null); + var result = bindCollectionInitializerElementAddMethod(elementInitializer, boundElementInitializerExpressions, collectionInitializerAddMethodBinder, diagnostics, implicitReceiver); - if (implicitReceiver.Type.IsDynamic()) +#if DEBUG + if (!result.HasErrors && + boundElementInitializerExpressions.Length == 1 && + boundElementInitializerExpressions[0] is not + ({ Type: null } or BoundLiteral or BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }) && + !implicitReceiver.Type.IsDynamic()) { - var hasErrors = ReportBadDynamicArguments(elementInitializer, boundElementInitializerExpressions, refKinds: default, diagnostics, queryClause: null); - - return new BoundDynamicCollectionElementInitializer( - elementInitializer, - applicableMethods: ImmutableArray.Empty, - implicitReceiver, - arguments: boundElementInitializerExpressions.SelectAsArray(e => BindToNaturalType(e, diagnostics)), - type: GetSpecialType(SpecialType.System_Void, diagnostics, elementInitializer), - hasErrors: hasErrors); - } - - // Receiver is early bound, find method Add and invoke it (may still be a dynamic invocation): + var d = BindingDiagnosticBag.GetInstance(); - var addMethodDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: diagnostics.AccumulatesDependencies); - var addMethodInvocation = collectionInitializerAddMethodBinder.MakeInvocationExpression( - elementInitializer, - implicitReceiver, - methodName: WellKnownMemberNames.CollectionInitializerAddMethodName, - args: boundElementInitializerExpressions, - diagnostics: addMethodDiagnostics); - copyRelevantAddMethodDiagnostics(addMethodDiagnostics, diagnostics); + // 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)); - if (addMethodInvocation.Kind == BoundKind.DynamicInvocation) - { - var dynamicInvocation = (BoundDynamicInvocation)addMethodInvocation; - return new BoundDynamicCollectionElementInitializer( - elementInitializer, - dynamicInvocation.ApplicableMethods, - implicitReceiver, - dynamicInvocation.Arguments, - dynamicInvocation.Type, - hasErrors: dynamicInvocation.HasAnyErrors); + d.Free(); } - else if (addMethodInvocation.Kind == BoundKind.Call) +#endif + return result; + + BoundExpression bindCollectionInitializerElementAddMethod( + SyntaxNode elementInitializer, + ImmutableArray boundElementInitializerExpressions, + Binder collectionInitializerAddMethodBinder, + BindingDiagnosticBag diagnostics, + BoundObjectOrCollectionValuePlaceholder implicitReceiver) { - var boundCall = (BoundCall)addMethodInvocation; + Debug.Assert(collectionInitializerAddMethodBinder != null); + Debug.Assert(collectionInitializerAddMethodBinder.Flags.Includes(BinderFlags.CollectionInitializerAddMethod)); + Debug.Assert(implicitReceiver != null); + Debug.Assert((object)implicitReceiver.Type != null); - // Either overload resolution succeeded for this call or it did not. If it - // did not succeed then we've stashed the original method symbols from the - // method group, and we should use those as the symbols displayed for the - // call. If it did succeed then we did not stash any symbols. - if (boundCall.HasErrors && !boundCall.OriginalMethodsOpt.IsDefault) + if (implicitReceiver.Type.IsDynamic()) { - return boundCall; + var hasErrors = ReportBadDynamicArguments(elementInitializer, boundElementInitializerExpressions, refKinds: default, diagnostics, queryClause: null); + + return new BoundDynamicCollectionElementInitializer( + elementInitializer, + applicableMethods: ImmutableArray.Empty, + implicitReceiver, + arguments: boundElementInitializerExpressions.SelectAsArray(e => BindToNaturalType(e, diagnostics)), + type: GetSpecialType(SpecialType.System_Void, diagnostics, elementInitializer), + hasErrors: hasErrors); } - return new BoundCollectionElementInitializer( + // Receiver is early bound, find method Add and invoke it (may still be a dynamic invocation): + + var addMethodDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: diagnostics.AccumulatesDependencies); + var addMethodInvocation = collectionInitializerAddMethodBinder.MakeInvocationExpression( elementInitializer, - boundCall.Method, - boundCall.Arguments, - boundCall.ReceiverOpt, - boundCall.Expanded, - boundCall.ArgsToParamsOpt, - boundCall.DefaultArguments, - boundCall.InvokedAsExtensionMethod, - boundCall.ResultKind, - boundCall.Type, - boundCall.HasAnyErrors) - { WasCompilerGenerated = true }; - } - else - { - Debug.Assert(addMethodInvocation.Kind == BoundKind.BadExpression); - return addMethodInvocation; + implicitReceiver, + methodName: WellKnownMemberNames.CollectionInitializerAddMethodName, + args: boundElementInitializerExpressions, + diagnostics: addMethodDiagnostics); + copyRelevantAddMethodDiagnostics(addMethodDiagnostics, diagnostics); + + if (addMethodInvocation.Kind == BoundKind.DynamicInvocation) + { + var dynamicInvocation = (BoundDynamicInvocation)addMethodInvocation; + return new BoundDynamicCollectionElementInitializer( + elementInitializer, + dynamicInvocation.ApplicableMethods, + implicitReceiver, + dynamicInvocation.Arguments, + dynamicInvocation.Type, + hasErrors: dynamicInvocation.HasAnyErrors); + } + else if (addMethodInvocation.Kind == BoundKind.Call) + { + var boundCall = (BoundCall)addMethodInvocation; + + // Either overload resolution succeeded for this call or it did not. If it + // did not succeed then we've stashed the original method symbols from the + // method group, and we should use those as the symbols displayed for the + // call. If it did succeed then we did not stash any symbols. + if (boundCall.HasErrors && !boundCall.OriginalMethodsOpt.IsDefault) + { + return boundCall; + } + + return new BoundCollectionElementInitializer( + elementInitializer, + boundCall.Method, + boundCall.Arguments, + boundCall.ReceiverOpt, + boundCall.Expanded, + boundCall.ArgsToParamsOpt, + boundCall.DefaultArguments, + boundCall.InvokedAsExtensionMethod, + boundCall.ResultKind, + boundCall.Type, + boundCall.HasAnyErrors) + { WasCompilerGenerated = true }; + } + else + { + Debug.Assert(addMethodInvocation.Kind == BoundKind.BadExpression); + return addMethodInvocation; + } } static void copyRelevantAddMethodDiagnostics(BindingDiagnosticBag source, BindingDiagnosticBag target) @@ -6111,6 +6146,13 @@ protected BoundExpression BindClassCreationExpression( TypeSymbol initializerTypeOpt = null, bool wasTargetTyped = false) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableConstructor check + // this function should be kept in sync with HasCollectionExpressionApplicableConstructor. + // + BoundExpression result = null; bool hasErrors = type.IsErrorType(); if (type.IsAbstract) @@ -6244,6 +6286,15 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( in CompoundUseSiteInfo overloadResolutionUseSiteInfo, BindingDiagnosticBag diagnostics) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableConstructor check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableConstructor.bindClassCreationExpressionContinued, + // assuming that it only needs to cover scenario with no explicit arguments and no initializers. + // + ReportConstructorUseSiteDiagnostics(typeNode.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); if (memberResolutionResult.IsNotNull) @@ -6314,6 +6365,14 @@ private BoundExpression CreateBadClassCreationExpression( in CompoundUseSiteInfo overloadResolutionUseSiteInfo, BindingDiagnosticBag diagnostics) { + // + // !!! ATTENTION !!! + // + // In terms of reported errors this function should be kept in sync with local function + // HasCollectionExpressionApplicableConstructor.reportAdditionalDiagnosticsForOverloadResolutionFailure, + // assuming that it only needs to cover scenario with no explicit arguments and no initializers. + // + ReportConstructorUseSiteDiagnostics(typeNode.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); if (memberResolutionResult.IsNotNull) @@ -6535,29 +6594,39 @@ private BoundExpression BindTypeParameterCreationExpression(ObjectCreationExpres } #nullable enable - private BoundExpression BindTypeParameterCreationExpression( - SyntaxNode node, TypeParameterSymbol typeParameter, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax? initializerOpt, - SyntaxNode typeSyntax, bool wasTargetTyped, BindingDiagnosticBag diagnostics) + private static bool TypeParameterHasParameterlessConstructor(SyntaxNode node, TypeParameterSymbol typeParameter, BindingDiagnosticBag diagnostics) { if (!typeParameter.HasConstructorConstraint && !typeParameter.IsValueType) { diagnostics.Add(ErrorCode.ERR_NoNewTyvar, node.Location, typeParameter); + return false; } - else if (analyzedArguments.Arguments.Count > 0) - { - diagnostics.Add(ErrorCode.ERR_NewTyvarWithArgs, node.Location, typeParameter); - } - else + + return true; + } + + private BoundExpression BindTypeParameterCreationExpression( + SyntaxNode node, TypeParameterSymbol typeParameter, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax? initializerOpt, + SyntaxNode typeSyntax, bool wasTargetTyped, BindingDiagnosticBag diagnostics) + { + if (TypeParameterHasParameterlessConstructor(node, typeParameter, diagnostics)) { - var boundInitializerOpt = initializerOpt == null ? - null : - BindInitializerExpression( - syntax: initializerOpt, - type: typeParameter, - typeSyntax: typeSyntax, - isForNewInstance: true, - diagnostics: diagnostics); - return new BoundNewT(node, boundInitializerOpt, wasTargetTyped, typeParameter); + if (analyzedArguments.Arguments.Count > 0) + { + diagnostics.Add(ErrorCode.ERR_NewTyvarWithArgs, node.Location, typeParameter); + } + else + { + var boundInitializerOpt = initializerOpt == null ? + null : + BindInitializerExpression( + syntax: initializerOpt, + type: typeParameter, + typeSyntax: typeSyntax, + isForNewInstance: true, + diagnostics: diagnostics); + return new BoundNewT(node, boundInitializerOpt, wasTargetTyped, typeParameter); + } } return MakeBadExpressionForObjectCreation(node, typeParameter, analyzedArguments, initializerOpt, typeSyntax, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 49db57d26ecca..df42b52940a35 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -88,6 +88,14 @@ internal BoundExpression MakeInvocationExpression( bool ignoreNormalFormIfHasValidParamsParameter = false, bool searchExtensionMethodsIfNecessary = true) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.makeInvocationExpression + // + Debug.Assert(receiver != null); Debug.Assert(names.IsDefault || names.Length == args.Length); @@ -97,29 +105,22 @@ internal BoundExpression MakeInvocationExpression( // The other consumers of this helper (await and collection initializers) require the target member to be a method. if (!allowFieldsAndProperties && (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess)) { + ReportMakeInvocationExpressionBadMemberKind(node, methodName, boundExpression, diagnostics); + Symbol symbol; - MessageID msgId; if (boundExpression.Kind == BoundKind.FieldAccess) { - msgId = MessageID.IDS_SK_FIELD; symbol = ((BoundFieldAccess)boundExpression).FieldSymbol; } else { - msgId = MessageID.IDS_SK_PROPERTY; symbol = ((BoundPropertyAccess)boundExpression).PropertySymbol; } - diagnostics.Add( - ErrorCode.ERR_BadSKknown, - node.Location, - methodName, - msgId.Localize(), - MessageID.IDS_SK_METHOD.Localize()); - return BadExpression(node, LookupResultKind.Empty, ImmutableArray.Create(symbol), args.Add(receiver), wasCompilerGenerated: true); } + Debug.Assert(allowFieldsAndProperties || boundExpression.Kind is (BoundKind.MethodGroup or BoundKind.BadExpression)); boundExpression = CheckValue(boundExpression, BindValueKind.RValueOrMethodGroup, diagnostics); boundExpression.WasCompilerGenerated = true; @@ -151,6 +152,26 @@ internal BoundExpression MakeInvocationExpression( analyzedArguments.Free(); return result; } + + private static void ReportMakeInvocationExpressionBadMemberKind(SyntaxNode node, string methodName, BoundExpression boundExpression, BindingDiagnosticBag diagnostics) + { + MessageID msgId; + if (boundExpression.Kind == BoundKind.FieldAccess) + { + msgId = MessageID.IDS_SK_FIELD; + } + else + { + msgId = MessageID.IDS_SK_PROPERTY; + } + + diagnostics.Add( + ErrorCode.ERR_BadSKknown, + node.Location, + methodName, + msgId.Localize(), + MessageID.IDS_SK_METHOD.Localize()); + } #nullable disable /// @@ -304,6 +325,14 @@ private BoundExpression BindInvocationExpression( CSharpSyntaxNode queryClause = null, bool ignoreNormalFormIfHasValidParamsParameter = false) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.bindInvocationExpression + // + BoundExpression result; NamedTypeSymbol delegateType; @@ -359,6 +388,14 @@ 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; @@ -665,6 +702,14 @@ private BoundExpression BindMethodGroupInvocation( bool ignoreNormalFormIfHasValidParamsParameter, out bool anyApplicableCandidates) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.bindMethodGroupInvocation + // + BoundExpression result = null; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var resolution = this.ResolveMethodGroup( @@ -774,20 +819,7 @@ private BoundExpression BindMethodGroupInvocation( } else { - if (HasApplicableConditionalMethod(finalApplicableCandidates)) - { - // warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime - // because one or more applicable overloads are conditional methods - Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name); - } - - if (finalApplicableCandidates.Length != 1 && - HasApplicableMemberWithPossiblyExpandedNonArrayParamsCollection(resolution.AnalyzedArguments.Arguments, finalApplicableCandidates)) - { - Error(diagnostics, - ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, - syntax, methodGroup.Name); - } + ReportDynamicInvocationWarnings(syntax, methodGroup, diagnostics, resolution, finalApplicableCandidates); result = BindDynamicInvocation(syntax, methodGroup, resolution.AnalyzedArguments, finalApplicableCandidates.SelectAsArray(r => r.Member), diagnostics, queryClause); } @@ -809,6 +841,24 @@ private BoundExpression BindMethodGroupInvocation( return result; } + private void ReportDynamicInvocationWarnings(SyntaxNode syntax, BoundMethodGroup methodGroup, BindingDiagnosticBag diagnostics, MethodGroupResolution resolution, ImmutableArray> finalApplicableCandidates) + { + if (HasApplicableConditionalMethod(finalApplicableCandidates)) + { + // warning CS1974: The dynamically dispatched call to method 'Goo' may fail at runtime + // because one or more applicable overloads are conditional methods + Error(diagnostics, ErrorCode.WRN_DynamicDispatchToConditionalMethod, syntax, methodGroup.Name); + } + + if (finalApplicableCandidates.Length != 1 && + HasApplicableMemberWithPossiblyExpandedNonArrayParamsCollection(resolution.AnalyzedArguments.Arguments, finalApplicableCandidates)) + { + Error(diagnostics, + ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, + syntax, methodGroup.Name); + } + } + private bool IsAmbiguousDynamicParamsArgument(ArrayBuilder arguments, MemberResolutionResult candidate, out SyntaxNode argumentSyntax) where TMethodOrPropertySymbol : Symbol { @@ -834,17 +884,14 @@ private bool IsAmbiguousDynamicParamsArgument(ArrayBuil return false; } - private BoundExpression TryEarlyBindSingleCandidateInvocationWithDynamicArgument( + private bool CanEarlyBindSingleCandidateInvocationWithDynamicArgument( SyntaxNode syntax, - SyntaxNode expression, - string methodName, BoundMethodGroup boundMethodGroup, BindingDiagnosticBag diagnostics, - CSharpSyntaxNode queryClause, MethodGroupResolution resolution, - MemberResolutionResult methodResolutionResult) + MemberResolutionResult methodResolutionResult, + MethodSymbol singleCandidate) { - MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember; if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && singleCandidate.IsGenericMethod) { // If we call an unconstructed generic function with a @@ -874,7 +921,7 @@ private BoundExpression TryEarlyBindSingleCandidateInvocationWithDynamicArgument syntax, singleCandidate.Name); } - return null; + return false; } if (IsAmbiguousDynamicParamsArgument(resolution.AnalyzedArguments.Arguments, methodResolutionResult, out SyntaxNode argumentSyntax)) @@ -898,6 +945,34 @@ private BoundExpression TryEarlyBindSingleCandidateInvocationWithDynamicArgument argumentSyntax, singleCandidate.Parameters.Last().Name, singleCandidate.Name); } + return false; + } + + return true; + } + + private BoundExpression TryEarlyBindSingleCandidateInvocationWithDynamicArgument( + SyntaxNode syntax, + SyntaxNode expression, + string methodName, + BoundMethodGroup boundMethodGroup, + BindingDiagnosticBag diagnostics, + CSharpSyntaxNode queryClause, + MethodGroupResolution resolution, + MemberResolutionResult methodResolutionResult) + { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.tryEarlyBindSingleCandidateInvocationWithDynamicArgument + // + + MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember; + + if (!CanEarlyBindSingleCandidateInvocationWithDynamicArgument(syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) + { return null; } @@ -1060,6 +1135,14 @@ private BoundCall BindInvocationExpressionContinued( BindingDiagnosticBag diagnostics, CSharpSyntaxNode queryClause = null) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.bindInvocationExpressionContinued + // + Debug.Assert(node != null); Debug.Assert(methodGroup != null); Debug.Assert(methodGroup.Error == null); @@ -1519,14 +1602,66 @@ internal void BindDefaultArgumentsAndParamsCollection( BoundCollectionExpression converted; if (!conversion.Exists) { - // PROTOTYPE(ParamsCollections): Add test if this code path is reachable + 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); - converted = ConvertCollectionExpression(unconvertedCollection, collectionType, conversion, diagnostics); + + 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, IsParamsCollection = 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( diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index ebd58419791c6..9528274cc497e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -366,7 +366,7 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy var isParams = paramsKeyword.Kind() != SyntaxKind.None; // UNDONE: Where do we report improper use of pointer types? - ParameterHelpers.ReportParameterErrors(this, owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i), + ParameterHelpers.ReportParameterErrors(owner: null, paramSyntax, ordinal: i, lastParameterIndex: lambda.ParameterCount - 1, isParams: isParams, lambda.ParameterTypeWithAnnotations(i), lambda.RefKind(i), lambda.DeclaredScope(i), containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Binder/ParamsCollectionTypeInProgressBinder.cs b/src/Compilers/CSharp/Portable/Binder/ParamsCollectionTypeInProgressBinder.cs new file mode 100644 index 0000000000000..20aca10d2f0c6 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/ParamsCollectionTypeInProgressBinder.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// This binder keeps track of the type for which we are trying to + /// determine whether it is a valid 'params' collection type. + /// + internal sealed class ParamsCollectionTypeInProgressBinder : Binder + { + private readonly NamedTypeSymbol _inProgress; + + internal ParamsCollectionTypeInProgressBinder(NamedTypeSymbol inProgress, Binder next) + : base(next, next.Flags | BinderFlags.CollectionExpressionConversionValidation) + { + Debug.Assert(inProgress is not null); + + _inProgress = inProgress; + } + + internal override NamedTypeSymbol ParamsCollectionTypeInProgress => _inProgress; + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index cbae2e9f7df79..96888f4a2ff91 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -106,24 +106,34 @@ internal DeconstructionUncommonData(DeconstructMethodInfo deconstructMethodInfoO private sealed class CollectionExpressionUncommonData : NestedUncommonData { - internal CollectionExpressionUncommonData(CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol elementType, ImmutableArray elementConversions) : + internal CollectionExpressionUncommonData( + CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol elementType, + MethodSymbol? constructor, bool constructorUsedInExpandedForm, + ImmutableArray elementConversions) : base(elementConversions) { Debug.Assert(collectionExpressionTypeKind != CollectionExpressionTypeKind.None); Debug.Assert(elementType is { }); CollectionExpressionTypeKind = collectionExpressionTypeKind; ElementType = elementType; + Constructor = constructor; + ConstructorUsedInExpandedForm = constructorUsedInExpandedForm; } internal readonly CollectionExpressionTypeKind CollectionExpressionTypeKind; internal readonly TypeSymbol ElementType; + internal readonly MethodSymbol? Constructor; + internal readonly bool ConstructorUsedInExpandedForm; } - internal static Conversion CreateCollectionExpressionConversion(CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol elementType, ImmutableArray elementConversions) + internal static Conversion CreateCollectionExpressionConversion( + CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol elementType, + MethodSymbol? constructor, bool constructorUsedInExpandedForm, + ImmutableArray elementConversions) { return new Conversion( ConversionKind.CollectionExpression, - new CollectionExpressionUncommonData(collectionExpressionTypeKind, elementType, elementConversions)); + new CollectionExpressionUncommonData(collectionExpressionTypeKind, elementType, constructor, constructorUsedInExpandedForm, elementConversions)); } private Conversion( @@ -530,14 +540,19 @@ internal DeconstructMethodInfo DeconstructionInfo } } - internal CollectionExpressionTypeKind GetCollectionExpressionTypeKind(out TypeSymbol? elementType) + internal CollectionExpressionTypeKind GetCollectionExpressionTypeKind(out TypeSymbol? elementType, out MethodSymbol? constructor, out bool isExpanded) { if (_uncommonData is CollectionExpressionUncommonData collectionExpressionData) { elementType = collectionExpressionData.ElementType; + constructor = collectionExpressionData.Constructor; + isExpanded = collectionExpressionData.ConstructorUsedInExpandedForm; return collectionExpressionData.CollectionExpressionTypeKind; } + elementType = null; + constructor = null; + isExpanded = false; return CollectionExpressionTypeKind.None; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index a0cd26ff64d66..fc290d94d1524 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -183,15 +183,18 @@ protected override Conversion GetCollectionExpressionConversion( Debug.Assert(elementType is { }); var elements = node.Elements; + MethodSymbol? constructor = null; + bool isExpanded = false; + if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable) { - if (!_binder.HasCollectionExpressionApplicableConstructor(syntax, targetType, BindingDiagnosticBag.Discarded)) + if (!_binder.HasCollectionExpressionApplicableConstructor(syntax, targetType, out constructor, out isExpanded, BindingDiagnosticBag.Discarded)) { return Conversion.NoConversion; } if (elements.Length > 0 && - !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, elementType, BindingDiagnosticBag.Discarded)) + !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, elementType, addMethods: out _, BindingDiagnosticBag.Discarded)) { return Conversion.NoConversion; } @@ -210,7 +213,7 @@ protected override Conversion GetCollectionExpressionConversion( builder.Add(elementConversion); } - return Conversion.CreateCollectionExpressionConversion(collectionTypeKind, elementType, builder.ToImmutableAndFree()); + return Conversion.CreateCollectionExpressionConversion(collectionTypeKind, elementType, constructor, isExpanded, builder.ToImmutableAndFree()); Conversion convertElement(BoundNode element, TypeSymbol elementType, ref CompoundUseSiteInfo useSiteInfo) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 5327c521067a1..6bcac760fce74 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1177,28 +1177,42 @@ public static bool IsValidParams(Binder binder, Symbol member) public static bool TryInferParamsCollectionIterationType(Binder binder, TypeSymbol type, out TypeWithAnnotations elementType) { - if (type.IsSZArray()) - { - elementType = ((ArrayTypeSymbol)type).ElementTypeWithAnnotations; - return true; - } + var collectionTypeKind = ConversionsBase.GetCollectionExpressionTypeKind(binder.Compilation, type, out elementType); - if (ConversionsBase.GetCollectionExpressionTypeKind(binder.Compilation, type, out elementType) != CollectionExpressionTypeKind.None) + switch (collectionTypeKind) { - if (elementType.Type is null) - { - // PROTOTYPE(ParamsCollections): Test this code path - binder.TryGetCollectionIterationType(CSharpSyntaxTree.Dummy.GetRoot(), type, out elementType); - } + case CollectionExpressionTypeKind.None: + return false; - if (elementType.Type is not null) - { - return true; - } + case CollectionExpressionTypeKind.ImplementsIEnumerable: + case CollectionExpressionTypeKind.CollectionBuilder: + { + SyntaxNode syntax = CSharpSyntaxTree.Dummy.GetRoot(); + binder.TryGetCollectionIterationType(syntax, type, out elementType); + + if (elementType.Type is null) + { + return false; + } + + if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable) + { + if (!binder.HasCollectionExpressionApplicableConstructor(syntax, type, constructor: out _, isExpanded: out _, BindingDiagnosticBag.Discarded)) + { + return false; + } + + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) + { + return false; + } + } + } + break; } - elementType = default; - return false; + Debug.Assert(elementType.Type is { }); + return true; } /// @@ -2744,9 +2758,9 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy private bool IsBetterCollectionExpressionConversion(TypeSymbol t1, Conversion conv1, TypeSymbol t2, Conversion conv2, ref CompoundUseSiteInfo useSiteInfo) { TypeSymbol elementType1; - var kind1 = conv1.GetCollectionExpressionTypeKind(out elementType1); + var kind1 = conv1.GetCollectionExpressionTypeKind(out elementType1, out _, out _); TypeSymbol elementType2; - var kind2 = conv2.GetCollectionExpressionTypeKind(out elementType2); + var kind2 = conv2.GetCollectionExpressionTypeKind(out elementType2, out _, out _); return IsBetterCollectionExpressionConversion(t1, kind1, elementType1, t2, kind2, elementType2, ref useSiteInfo); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index a7bd7f5d5609b..1088cd9013d80 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7872,4 +7872,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8135db3ebd448..adab45b4850e3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2290,6 +2290,8 @@ internal enum ErrorCode WRN_DynamicDispatchToParamsCollectionMethod = 9503, WRN_DynamicDispatchToParamsCollectionIndexer = 9504, WRN_DynamicDispatchToParamsCollectionConstructor = 9505, + ERR_ParamsCollectionInfiniteChainOfConstructorCalls = 9506, + ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember = 9507, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 196f064af6c7c..a26ee137dc0e5 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2422,6 +2422,8 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod: case ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer: case ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor: + case ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls: + case ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 1764b0b70ae24..bcd72d80f06e8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -34,7 +34,7 @@ private BoundExpression RewriteCollectionExpressionConversion(Conversion convers _factory.Syntax = node.Syntax; try { - var collectionTypeKind = conversion.GetCollectionExpressionTypeKind(out var elementType); + var collectionTypeKind = conversion.GetCollectionExpressionTypeKind(out var elementType, out _, out _); switch (collectionTypeKind) { case CollectionExpressionTypeKind.ImplementsIEnumerable: diff --git a/src/Compilers/CSharp/Portable/Symbols/CompletionPart.cs b/src/Compilers/CSharp/Portable/Symbols/CompletionPart.cs index c69369a4361fa..da3009c5a969c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/CompletionPart.cs +++ b/src/Compilers/CSharp/Portable/Symbols/CompletionPart.cs @@ -85,7 +85,9 @@ internal enum CompletionPart StartDefaultSyntaxValue = 1 << 11, EndDefaultSyntaxValue = 1 << 12, EndDefaultSyntaxValueDiagnostics = 1 << 13, - ComplexParameterSymbolAll = Attributes | StartDefaultSyntaxValue | EndDefaultSyntaxValue | EndDefaultSyntaxValueDiagnostics, + StartParamsValidation = 1 << 14, + EndParamsValidation = 1 << 15, + ComplexParameterSymbolAll = Attributes | StartDefaultSyntaxValue | EndDefaultSyntaxValue | EndDefaultSyntaxValueDiagnostics | StartParamsValidation | EndParamsValidation, // For type parameter symbols TypeParameterConstraints = 1 << 11, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 26057fc7fcb26..66cb590e83464 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -191,7 +191,9 @@ private static ImmutableArray MakeParameters _syntaxRef; - private ParameterSyntax CSharpSyntaxNode => (ParameterSyntax)_syntaxRef?.GetSyntax(); + private ParameterSyntax ParameterSyntax => (ParameterSyntax)_syntaxRef?.GetSyntax(); public override bool IsDiscard => false; @@ -251,7 +251,7 @@ private ConstantValue DefaultSyntaxValue if (parameterEqualsValue is not null) { if (binder is not null && - GetDefaultValueSyntaxForIsNullableAnalysisEnabled(CSharpSyntaxNode) is { } valueSyntax) + GetDefaultValueSyntaxForIsNullableAnalysisEnabled(ParameterSyntax) is { } valueSyntax) { NullableWalker.AnalyzeIfNeeded(binder, parameterEqualsValue, valueSyntax, diagnostics.DiagnosticBag); } @@ -302,7 +302,7 @@ private Binder GetDefaultParameterValueBinder(SyntaxNode syntax) private void NullableAnalyzeParameterDefaultValueFromAttributes() { - var parameterSyntax = this.CSharpSyntaxNode; + var parameterSyntax = this.ParameterSyntax; if (parameterSyntax == null) { // If there is no syntax at all for the parameter, it means we are in a situation like @@ -354,7 +354,7 @@ private ConstantValue MakeDefaultExpression(BindingDiagnosticBag diagnostics, ou binder = null; parameterEqualsValue = null; - var parameterSyntax = this.CSharpSyntaxNode; + var parameterSyntax = this.ParameterSyntax; if (parameterSyntax == null) { return ConstantValue.NotAvailable; @@ -478,7 +478,7 @@ internal sealed override SyntaxList AttributeDeclarationLis { get { - var syntax = this.CSharpSyntaxNode; + var syntax = this.ParameterSyntax; return (syntax != null) ? syntax.AttributeLists : default(SyntaxList); } } @@ -891,7 +891,7 @@ private void DecodeDefaultParameterValueAttribute(AttributeDescription descripti { VerifyParamDefaultValueMatchesAttributeIfAny(value, syntax, diagnostics); - if (this.RefKind == RefKind.RefReadOnlyParameter && this.IsOptional && this.CSharpSyntaxNode.Default is null) + if (this.RefKind == RefKind.RefReadOnlyParameter && this.IsOptional && this.ParameterSyntax.Default is null) { // A default value is specified for 'ref readonly' parameter '{0}', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. diagnostics.Add(ErrorCode.WRN_RefReadonlyParameterDefaultValue, syntax, this.Name); @@ -1060,7 +1060,7 @@ private void ValidateCallerLineNumberAttribute(AttributeSyntax node, BindingDiag { // CS4024: The CallerLineNumberAttribute applied to parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments - diagnostics.Add(ErrorCode.WRN_CallerLineNumberParamForUnconsumedLocation, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerLineNumberParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (!compilation.Conversions.HasCallerLineNumberConversion(TypeWithAnnotations.Type, ref useSiteInfo)) { @@ -1088,7 +1088,7 @@ private void ValidateCallerFilePathAttribute(AttributeSyntax node, BindingDiagno { // CS4025: The CallerFilePathAttribute applied to parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments - diagnostics.Add(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo)) { @@ -1106,7 +1106,7 @@ private void ValidateCallerFilePathAttribute(AttributeSyntax node, BindingDiagno else if (IsCallerLineNumber) { // CS7082: The CallerFilePathAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute. - diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerFilePath, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerFilePath, node.Name.Location, ParameterSyntax.Identifier.ValueText); } diagnostics.Add(node.Name, useSiteInfo); @@ -1121,7 +1121,7 @@ private void ValidateCallerMemberNameAttribute(AttributeSyntax node, BindingDiag { // CS4026: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments - diagnostics.Add(ErrorCode.WRN_CallerMemberNameParamForUnconsumedLocation, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerMemberNameParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo)) { @@ -1139,12 +1139,12 @@ private void ValidateCallerMemberNameAttribute(AttributeSyntax node, BindingDiag else if (IsCallerLineNumber) { // CS7081: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute. - diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerMemberName, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerMemberName, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (IsCallerFilePath) { // CS7080: The CallerMemberNameAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerFilePathAttribute. - diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerMemberName, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerMemberName, node.Name.Location, ParameterSyntax.Identifier.ValueText); } diagnostics.Add(node.Name, useSiteInfo); @@ -1162,7 +1162,7 @@ private void ValidateCallerArgumentExpressionAttribute(AttributeSyntax node, CSh { // CS8966: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect because it applies to a // member that is used in contexts that do not allow optional arguments - diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (!compilation.Conversions.HasCallerInfoStringConversion(TypeWithAnnotations.Type, ref useSiteInfo)) { @@ -1180,28 +1180,28 @@ private void ValidateCallerArgumentExpressionAttribute(AttributeSyntax node, CSh else if (IsCallerLineNumber) { // CS8960: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute. - diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerArgumentExpression, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerLineNumberPreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (IsCallerFilePath) { // CS8961: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerFilePathAttribute. - diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerArgumentExpression, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerFilePathPreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (IsCallerMemberName) { // CS8962: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerMemberNameAttribute. - diagnostics.Add(ErrorCode.WRN_CallerMemberNamePreferredOverCallerArgumentExpression, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerMemberNamePreferredOverCallerArgumentExpression, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (attribute.CommonConstructorArguments.Length == 1 && GetEarlyDecodedWellKnownAttributeData()?.CallerArgumentExpressionParameterIndex == -1) { // CS8963: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is applied with an invalid parameter name. - diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, node.Name.Location, ParameterSyntax.Identifier.ValueText); } else if (GetEarlyDecodedWellKnownAttributeData()?.CallerArgumentExpressionParameterIndex == Ordinal) { // CS8965: The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect because it's self-referential. - diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeSelfReferential, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_CallerArgumentExpressionAttributeSelfReferential, node.Name.Location, ParameterSyntax.Identifier.ValueText); } diagnostics.Add(node.Name, useSiteInfo); @@ -1211,7 +1211,7 @@ private void ValidateCancellationTokenAttribute(AttributeSyntax node, BindingDia { if (needsReporting()) { - diagnostics.Add(ErrorCode.WRN_UnconsumedEnumeratorCancellationAttributeUsage, node.Name.Location, CSharpSyntaxNode.Identifier.ValueText); + diagnostics.Add(ErrorCode.WRN_UnconsumedEnumeratorCancellationAttributeUsage, node.Name.Location, ParameterSyntax.Identifier.ValueText); } bool needsReporting() @@ -1512,8 +1512,161 @@ internal override void ForceComplete(SourceLocation locationOpt, Predicate addMethods, diagnostics)) + { + return; + } + + Debug.Assert(!addMethods.IsDefaultOrEmpty); + + if (addMethods[0].IsStatic) // No need to check other methods, extensions are never mixed with instance methods + { + Debug.Assert(addMethods[0].IsExtensionMethod); + diagnostics.Add(ErrorCode.ERR_NoSuchMember, syntax, Type, WellKnownMemberNames.CollectionInitializerAddMethodName); + return; + } + + MethodSymbol? reportAsLessVisible = null; + + foreach (var addMethod in addMethods) + { + if (isAtLeastAsVisible(syntax, binder, addMethod, diagnostics)) + { + reportAsLessVisible = null; + break; + } + else + { + reportAsLessVisible ??= addMethod; + } + } + + if (reportAsLessVisible is not null) + { + diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, reportAsLessVisible, ContainingSymbol); + } + } + break; + + case CollectionExpressionTypeKind.CollectionBuilder: + { + var syntax = ParameterSyntax; + var binder = GetDefaultParameterValueBinder(syntax); // this binder is good for our purpose + + binder.TryGetCollectionIterationType(syntax, Type, out elementTypeWithAnnotations); + elementType = elementTypeWithAnnotations.Type; + if (elementType is null) + { + reportInvalidParams(diagnostics); + return; + } + + MethodSymbol? collectionBuilderMethod = binder.GetAndValidateCollectionBuilderMethod(syntax, (NamedTypeSymbol)Type, diagnostics, elementType: out _); + if (collectionBuilderMethod is null) + { + return; + } + + if (ContainingSymbol.ContainingSymbol is NamedTypeSymbol) // No need to check for lambdas or local function + { + checkIsAtLeastAsVisible(syntax, binder, collectionBuilderMethod, diagnostics); + } + } + break; + } + + Debug.Assert(elementType is { }); + + if (collectionTypeKind != CollectionExpressionTypeKind.Array) + { + MessageID.IDS_ParamsCollections.CheckFeatureAvailability(diagnostics, ParameterSyntax); + } + } + + bool isAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics) + { + var useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics); + + bool result = method.IsAsRestrictive(ContainingSymbol, ref useSiteInfo) && + method.ContainingType.IsAtLeastAsVisibleAs(ContainingSymbol, ref useSiteInfo); + + diagnostics.Add(syntax.Location, useSiteInfo); + return result; + } + + void checkIsAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics) + { + if (!isAtLeastAsVisible(syntax, binder, method, diagnostics)) + { + diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, method, ContainingSymbol); + } + } + + void reportInvalidParams(BindingDiagnosticBag diagnostics) + { + diagnostics.Add(ErrorCode.ERR_ParamsMustBeCollection, ParameterSyntax.Modifiers.First(static m => m.IsKind(SyntaxKind.ParamsKeyword)).GetLocation()); + } + } + +#nullable disable } internal sealed class SourceComplexParameterSymbol : SourceComplexParameterSymbolBase diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 2a47456a4c531..ad0792c691d08 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index cf295a88a86ee..f6ec4aadadafe 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index a627baba480eb..4cbbc130e2f64 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index fd1b9f550f133..e528c41f08af0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 61fcca589245f..9207684d1aac1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index de1067631a2db..9966c20084d9e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 50e6b26b321ff..1594c40cef43c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 78dd5179fae4d..ca79d48758eec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index c1ec309b37cc2..8d9807a539041 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 732ead8ee924a..57e91ca69f0e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a94e8f5dd7776..83563b75a7e72 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index e416f140ee0ac..cc9746fa5803c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 3a611195e0d18..65ae87c3995e9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1527,6 +1527,16 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + + + + Method '{0}' cannot be less visible than the member with params collection '{1}'. + Method '{0}' cannot be less visible than the member with params collection '{1}'. + + The params parameter must have a valid collection type The params parameter must have a valid collection type diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index ed39ef99aeb4a..bb1de18a9484a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -34010,5 +34010,246 @@ static string Invoke(Action a) IList.RemoveAt(0): System.NotSupportedException """); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] + public void UnsafeContext_01_Constructor() + { + string source1 = """ + using System.Collections; + using System.Collections.Generic; + + public class MyCollectionOfInt : IEnumerable + { + unsafe public MyCollectionOfInt(void* dummy = null){} + + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(int l) => Array.Add(l); + + public static implicit operator MyCollectionOfLong(MyCollectionOfInt c) => throw null; + } + + public class MyCollectionOfLong : IEnumerable + { + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); + } + + public class Overloads + { + public static void Test(MyCollectionOfInt a) + { + System.Console.WriteLine("Int"); + } + + public static void Test(MyCollectionOfLong a) + { + System.Console.WriteLine("Long"); + } + } + """; + + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); + + string source2 = """ + class Program + { + unsafe static void Main() + { + Overloads.Test([2, 3]); + } + } + """; + + var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugExe); + CompileAndVerify( + comp2, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: "Int"); + + string source3 = """ + class Program + { + static void Main() + { + Overloads.Test([2, 3]); + } + } + """; + + var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.UnsafeDebugExe); + + // PROTOTYPE(ParamsCollections): Confirm the behavior matches what we decided to do for https://github.com/dotnet/roslyn/issues/71840 + comp3.VerifyDiagnostics( + // (5,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Overloads.Test([2, 3]); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "[2, 3]").WithLocation(5, 24) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] + public void UnsafeContext_02_Add() + { + string source1 = """ + using System.Collections; + using System.Collections.Generic; + + public class MyCollectionOfInt : IEnumerable + { + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + unsafe public void Add(int l, void* dummy = null) => Array.Add(l); + + public static implicit operator MyCollectionOfLong(MyCollectionOfInt c) => throw null; + } + + public class MyCollectionOfLong : IEnumerable + { + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); + } + + public class Overloads + { + public static void Test(MyCollectionOfInt a) + { + System.Console.WriteLine("Int"); + } + + public static void Test(MyCollectionOfLong a) + { + System.Console.WriteLine("Long"); + } + } + """; + + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); + + string source2 = """ + class Program + { + unsafe static void Main() + { + Overloads.Test([2, 3]); + } + } + """; + + var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugExe); + CompileAndVerify( + comp2, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: "Int"); + + string source3 = """ + class Program + { + static void Main() + { + Overloads.Test([2, 3]); + } + } + """; + + var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.UnsafeDebugExe); + + // PROTOTYPE(ParamsCollections): Confirm the behavior matches what we decided to do for https://github.com/dotnet/roslyn/issues/71840 + comp3.VerifyDiagnostics( + // (5,25): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Overloads.Test([2, 3]); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "2").WithLocation(5, 25), + // (5,28): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Overloads.Test([2, 3]); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "3").WithLocation(5, 28) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71854")] + public void CreateMethod_InDifferentAssembly() + { + var myCollection_v0Source = """ +using System.Collections.Generic; + +public class MyCollection +{ + public long[] Array; + public IEnumerator GetEnumerator() => throw null; +} +"""; + + var myCollection_v0 = CreateCompilation(myCollection_v0Source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); + myCollection_v0.VerifyDiagnostics(); + + var builderSource = """ +using System; + +public class MyCollectionBuilder +{ + public static MyCollection Create(ReadOnlySpan items) => new MyCollection() { Array = items.ToArray() }; +} +"""; + + var builder = CreateCompilation(builderSource, references: [myCollection_v0.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + builder.VerifyDiagnostics(); + + var myCollectionSource = """ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +public class MyCollection +{ + public long[] Array; + public IEnumerator GetEnumerator() => throw null; +} +"""; + + var myCollection = CreateCompilation(myCollectionSource, references: [builder.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); + myCollection.VerifyDiagnostics(); + var myCollectionRef = myCollection.EmitToImageReference(); + + var src = """ +class Program +{ + static void Main() + { + Test([1]); + } + + static void Test(MyCollection a) + { + System.Console.WriteLine("{0}: {1}", a.Array.Length, a.Array[0]); + } +} +"""; + var comp = CreateCompilation(src, references: [myCollectionRef, builder.EmitToImageReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: IncludeExpectedOutput(@"1: 1")).VerifyDiagnostics(); + + comp = CreateCompilation(src, references: [myCollectionRef], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics( + // (5,14): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test([1]); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1]").WithArguments("Create", "long", "MyCollection").WithLocation(5, 14) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 87775d352c837..1e8cb9b2b1f13 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -362,7 +362,7 @@ void assertAttributeData(string name) } } - [Fact(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules] + [Fact] public void String() { var src = @" @@ -382,35 +382,23 @@ static void Test(params string a) "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - // PROTOTYPE(ParamsCollections): Note, there is no error at the declaration site, because - // according to https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#conversions, - // there is a conversion from a collection expression consisting of `char`s to a `string` type. - // Even though `string` lacks APIs needed to perform the conversion. - // Similar situation can happen with other types. Are we fine with this behavior, or should we - // enforce existence of at least some APIs at the declaration site? comp.VerifyDiagnostics( - // (6,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // (6,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params string)' // Test(); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test()").WithArguments("string", "0").WithLocation(6, 9), - // (7,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // Test('a'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('a')").WithArguments("string", "0").WithLocation(7, 9), - // (7,14): 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?) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params string)").WithLocation(6, 9), + // (7,14): error CS1503: Argument 1: cannot convert from 'char' to 'params string' // Test('a'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'a'").WithArguments("string", "Add").WithLocation(7, 14), - // (8,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // Test('b', 'c'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('b', 'c')").WithArguments("string", "0").WithLocation(8, 9), - // (8,14): 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?) - // Test('b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'b'").WithArguments("string", "Add").WithLocation(8, 14), - // (8,19): 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?) + Diagnostic(ErrorCode.ERR_BadArgType, "'a'").WithArguments("1", "char", "params string").WithLocation(7, 14), + // (8,9): error CS1501: No overload for method 'Test' takes 2 arguments // Test('b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'c'").WithArguments("string", "Add").WithLocation(8, 19) + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(8, 9), + // (11,22): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void Test(params string a) + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string a").WithArguments("string", "0").WithLocation(11, 22) ); } - [Fact(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules] + [Fact] public void String_InAttribute() { var src = @" @@ -431,24 +419,18 @@ public Test(params string a) {} var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( - // (2,2): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // [Test()] - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test()").WithArguments("string", "0").WithLocation(2, 2), - // (5,2): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // [Test('1')] - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('1')").WithArguments("string", "0").WithLocation(5, 2), - // (5,7): 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?) - // [Test('1')] - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'1'").WithArguments("string", "Add").WithLocation(5, 7), - // (8,2): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // [Test('2', '3')] - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('2', '3')").WithArguments("string", "0").WithLocation(8, 2), - // (8,7): 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?) - // [Test('2', '3')] - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'2'").WithArguments("string", "Add").WithLocation(8, 7), - // (8,12): 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?) - // [Test('2', '3')] - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'3'").WithArguments("string", "Add").WithLocation(8, 12) + // (2,2): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Test.Test(params string)' + // [Test()] + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test()").WithArguments("a", "Test.Test(params string)").WithLocation(2, 2), + // (5,7): error CS1503: Argument 1: cannot convert from 'char' to 'params string' + // [Test('1')] + Diagnostic(ErrorCode.ERR_BadArgType, "'1'").WithArguments("1", "char", "params string").WithLocation(5, 7), + // (8,2): error CS1729: 'Test' does not contain a constructor that takes 2 arguments + // [Test('2', '3')] + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "Test('2', '3')").WithArguments("Test", "2").WithLocation(8, 2), + // (13,17): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // public Test(params string a) {} + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string a").WithArguments("string", "0").WithLocation(13, 17) ); assertAttributeData("C1"); @@ -479,17 +461,12 @@ void assertAttributeData(string name) { var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); Assert.True(attributeData1.HasErrors); - - var c1Arg = attributeData1.ConstructorArguments.Single(); - Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); - Assert.Equal("System.String", c1Arg.Type.ToTestDisplayString()); - Assert.Null(c1Arg.Value); - Assert.Throws(() => c1Arg.Values); + Assert.Empty(attributeData1.ConstructorArguments); } } [Fact] - public void CreateMethod() + public void CreateMethod_01() { var src = """ using System; @@ -556,7 +533,7 @@ static void Test2() [Theory] [CombinatorialData] - public void CreateMethod_InAttribute(bool asStruct) + public void CreateMethod_02_InAttribute(bool asStruct) { var src = @" using System; @@ -639,19 +616,20 @@ void assertAttributeData(string name) } [Fact] - public void ImplementsIEnumerableT_01() + public void CreateMethod_03_NoElementType() { var src = """ -using System.Collections; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; -class MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +class MyCollection { - public List Array = new List(); - IEnumerator IEnumerable.GetEnumerator() => throw null; - IEnumerator IEnumerable.GetEnumerator() => throw null; - - public void Add(long l) => Array.Add(l); +} +class MyCollectionBuilder +{ + public static MyCollection Create(ReadOnlySpan items) => null; } class Program @@ -665,199 +643,149 @@ static void Main() static void Test(params MyCollection a) { - if (a.Array.Count == 0) - { - System.Console.WriteLine(a.Array.Count); - } - else - { - System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); - } - } - - static void Test2() - { - Test([2, 3]); } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, - sourceSymbolValidator: static (m) => - { - VerifyParams(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); - }, - symbolValidator: static (m) => - { - VerifyParamsAndAttribute(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); - }, - expectedOutput: @" -0 -1: 1 ... 1 -2: 2 ... 3 -").VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), + // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), + // (23,22): error CS0225: The params parameter must have a valid collection type + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(23, 22) + ); } [Fact] - public void ImplementsIEnumerableT_02() + public void CreateMethod_04_NoElementType() { var src = """ -using System.Collections; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; -class MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +class MyCollection { - IEnumerator IEnumerable.GetEnumerator() => throw null; - IEnumerator IEnumerable.GetEnumerator() => throw null; - - public IEnumerator GetEnumerator() => throw null; - - public void Add(long l) => throw null; - public void Add(string l) => throw null; +} +class MyCollectionBuilder +{ + public static MyCollection Create(ReadOnlySpan items) => null; } class Program { static void Main() { - Test("2", 3); - Test(["2", 3]); - Test("2"); - Test(["2"]); - Test(3); - Test([3]); - - MyCollection x1 = ["2"]; - MyCollection x2 = [3]; + Test(); + Test(1); + Test(2, 3); + new MyCollection().GetEnumerator(); } - static void Test(params MyCollection a) { } } + +static class Ext +{ + public static IEnumerator GetEnumerator(this MyCollection x) => throw null; +} """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (19,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' - // Test("2", 3); - Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(19, 19), - // (20,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' - // Test(["2", 3]); - Diagnostic(ErrorCode.ERR_BadArgType, @"[""2"", 3]").WithArguments("1", "collection expressions", "string").WithLocation(20, 14), - // (23,14): error CS1503: Argument 1: cannot convert from 'int' to 'string' - // Test(3); - Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("1", "int", "string").WithLocation(23, 14), - // (24,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' - // Test([3]); - Diagnostic(ErrorCode.ERR_BadArgType, "[3]").WithArguments("1", "collection expressions", "string").WithLocation(24, 14), - // (27,28): error CS0029: Cannot implicitly convert type 'int' to 'string' - // MyCollection x2 = [3]; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(27, 28) + comp.VerifyEmitDiagnostics( + // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), + // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), + // (23,22): error CS0225: The params parameter must have a valid collection type + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(23, 22) ); } - [Theory] - [CombinatorialData] - public void ImplementsIEnumerableT_03_InAttribute(bool asStruct) + [Fact] + public void CreateMethod_05_Missing() { - var src = @" + var src = """ using System; -using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; -" + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder), "Create")] +public class MyCollection { - IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); - IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); - - public void Add(long l) {} + public IEnumerator GetEnumerator() => throw null; } - -[Test()] -class C1; - -[Test(1)] -class C2; - -[Test(2, 3)] -class C3; - -class Test : System.Attribute +public class MyCollectionBuilder { - public Test(params MyCollection a) {} } -"; - var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - - comp.VerifyDiagnostics( - // (14,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test()] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(14, 2), - // (17,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test(1)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(17, 2), - // (20,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test(2, 3)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(20, 2) - ); - - assertAttributeData("C1"); - assertAttributeData("C2"); - assertAttributeData("C3"); - - var tree = comp.SyntaxTrees.Single(); - var nodes = tree.GetRoot().DescendantNodes().OfType().ToArray(); - Assert.Equal(3, nodes.Length); - - var model = comp.GetSemanticModel(tree); - - foreach (LiteralExpressionSyntax expression in nodes) - { - assertTypeInfo(expression); - } - void assertTypeInfo(LiteralExpressionSyntax expression) - { - var typeInfo = model.GetTypeInfo(expression); - Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); - Assert.Equal("System.Int64", typeInfo.ConvertedType.ToTestDisplayString()); - - Assert.True(model.GetConversion(expression).IsNumeric); - } +public class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } - void assertAttributeData(string name) - { - var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); - Assert.True(attributeData1.HasErrors); + public static void Test(params MyCollection a) + { + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - var c1Arg = attributeData1.ConstructorArguments.Single(); - Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); - Assert.Equal("MyCollection", c1Arg.Type.ToTestDisplayString()); - Assert.Null(c1Arg.Value); - Assert.Throws(() => c1Arg.Values); - } + comp.VerifyEmitDiagnostics( + // (18,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(18, 9), + // (19,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(1); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(19, 9), + // (20,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(20, 9), + // (23,29): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(23, 29) + ); } [Fact] - public void ImplementsIEnumerable_01() + public void CreateMethod_06_InconsistentAccessibility() { var src = """ -using System.Collections; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; -class MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +public class MyCollection { - public List Array = new List(); - IEnumerator IEnumerable.GetEnumerator() => throw null; - - public void Add(object l) => Array.Add(l); + public IEnumerator GetEnumerator() => throw null; +} +public class MyCollectionBuilder +{ + internal static MyCollection Create(ReadOnlySpan items) => null; } -class Program +public class Program { static void Main() { @@ -866,174 +794,301 @@ static void Main() Test(2, 3); } - static void Test(params MyCollection a) - { - if (a.Array.Count == 0) - { - System.Console.WriteLine(a.Array.Count); - } - else - { - System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); - } - } - - static void Test2() + public static void Test(params MyCollection a) { - Test([2, 3]); } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - CompileAndVerify( - comp, - sourceSymbolValidator: static (m) => - { - VerifyParams(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); - }, - symbolValidator: static (m) => - { - VerifyParamsAndAttribute(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); - }, - expectedOutput: @" -0 -1: 1 ... 1 -2: 2 ... 3 -").VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (24,29): error CS9507: Method 'MyCollectionBuilder.Create(ReadOnlySpan)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollectionBuilder.Create(System.ReadOnlySpan)", "Program.Test(params MyCollection)").WithLocation(24, 29) + ); } [Fact] - public void ImplementsIEnumerable_02() + public void CreateMethod_07_InconsistentAccessibility() { var src = """ -using System.Collections; +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; -class MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder1), nameof(MyCollectionBuilder1.Create))] +public class MyCollection1 { - IEnumerator IEnumerable.GetEnumerator() => throw null; - - public IEnumerator GetEnumerator() => throw null; - public void Add(object l) => throw null; + public IEnumerator GetEnumerator() => throw null; +} +internal class MyCollectionBuilder1 +{ + public static MyCollection1 Create(ReadOnlySpan items) => null; } -class Program +[CollectionBuilder(typeof(MyCollectionBuilder2), nameof(MyCollectionBuilder2.Create))] +public class MyCollection2 +{ + public IEnumerator GetEnumerator() => throw null; +} +internal class MyCollectionBuilder2 +{ + public static MyCollection2 Create(ReadOnlySpan items) => null; +} + +public class Program { static void Main() { - Test("2", 3); - Test(["2", 3]); + Test1(); + Test1(1); + Test1(2, 3); + + Test2(); + Test2(1); + Test2(2, 3); } - static void Test(params MyCollection a) + public static void Test1(params MyCollection1 a) + { + } + + internal static void Test2(params MyCollection2 a) { } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (16,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' - // Test("2", 3); - Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(16, 19), - // (17,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' - // Test(["2", 3]); - Diagnostic(ErrorCode.ERR_BadArgType, @"[""2"", 3]").WithArguments("1", "collection expressions", "string").WithLocation(17, 14) + comp.VerifyEmitDiagnostics( + // (38,30): error CS9507: Method 'MyCollectionBuilder1.Create(ReadOnlySpan)' cannot be less visible than the member with params collection 'Program.Test1(params MyCollection1)'. + // public static void Test1(params MyCollection1 a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection1 a").WithArguments("MyCollectionBuilder1.Create(System.ReadOnlySpan)", "Program.Test1(params MyCollection1)").WithLocation(38, 30) ); } - [Theory] - [CombinatorialData] - public void ImplementsIEnumerable_03_InAttribute(bool asStruct) + [Fact] + public void CreateMethod_08_Inaccessible() { - var src = @" + var src = """ using System; -using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; -" + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable +[CollectionBuilder(typeof(MyCollectionBuilder), "Create")] +public class MyCollection { - IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); + public IEnumerator GetEnumerator() => throw null; +} +public class MyCollectionBuilder +{ + protected static MyCollection Create(ReadOnlySpan items) => null; +} - public void Add(object l) {} +public class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + public static void Test(params MyCollection a) + { + } } +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); -[Test()] -class C1; + comp.VerifyEmitDiagnostics( + // (19,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(19, 9), + // (20,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(1); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(20, 9), + // (21,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(21, 9), + // (24,29): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(24, 29) + ); + } -[Test(1)] -class C2; + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71854")] + public void CreateMethod_09_InDifferentAssembly() + { + var myCollection_v0Source = """ +using System.Collections.Generic; -[Test(2, 3)] -class C3; +public class MyCollection +{ + public long[] Array; + public IEnumerator GetEnumerator() => throw null; +} +"""; -class Test : System.Attribute + var myCollection_v0 = CreateCompilation(myCollection_v0Source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); + myCollection_v0.VerifyDiagnostics(); + + var builderSource = """ +using System; + +public class MyCollectionBuilder { - public Test(params MyCollection a) {} + public static MyCollection Create(ReadOnlySpan items) => new MyCollection() { Array = items.ToArray() }; } -"; - var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); +"""; + + var builder = CreateCompilation(builderSource, references: [myCollection_v0.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + builder.VerifyDiagnostics(); + + var myCollectionSource = """ +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +public class MyCollection +{ + public long[] Array; + public IEnumerator GetEnumerator() => throw null; +} +"""; + + var myCollection = CreateCompilation(myCollectionSource, references: [builder.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, assemblyName: "Collection"); + myCollection.VerifyDiagnostics(); + var myCollectionRef = myCollection.EmitToImageReference(); + + var src = """ +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + if (a.Array.Length == 0) + { + System.Console.WriteLine(a.Array.Length); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Length, a.Array[0], a.Array[^1]); + } + } +} +"""; + var comp = CreateCompilation(src, references: [myCollectionRef, builder.EmitToImageReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + + comp = CreateCompilation(src, references: [myCollectionRef], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + // The error improvement is tracked by https://github.com/dotnet/roslyn/issues/71854 comp.VerifyDiagnostics( - // (12,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test()] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(12, 2), - // (15,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test(1)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(15, 2), - // (18,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type - // [Test(2, 3)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(18, 2) + // (5,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test()").WithArguments("Create", "long", "MyCollection").WithLocation(5, 9), + // (6,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(1); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(1)").WithArguments("Create", "long", "MyCollection").WithLocation(6, 9), + // (7,9): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "Test(2, 3)").WithArguments("Create", "long", "MyCollection").WithLocation(7, 9), + // (10,22): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method with a single parameter of type 'ReadOnlySpan' and return type 'MyCollection'. + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "params MyCollection a").WithArguments("Create", "long", "MyCollection").WithLocation(10, 22) ); + } - assertAttributeData("C1"); - assertAttributeData("C2"); - assertAttributeData("C3"); - - var tree = comp.SyntaxTrees.Single(); - var nodes = tree.GetRoot().DescendantNodes().OfType().ToArray(); - Assert.Equal(3, nodes.Length); + [Fact] + public void CreateMethod_10_NotExtensionGetEnumerator() + { + var src = """ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; - var model = comp.GetSemanticModel(tree); +[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] +class MyCollection +{ +} - foreach (LiteralExpressionSyntax expression in nodes) - { - assertTypeInfo(expression); - } +static class Ext +{ + public static IEnumerator GetEnumerator(this MyCollection c) => throw null; +} - void assertTypeInfo(LiteralExpressionSyntax expression) - { - var typeInfo = model.GetTypeInfo(expression); - Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); - Assert.Equal("System.Object", typeInfo.ConvertedType.ToTestDisplayString()); +class MyCollectionBuilder +{ + public static MyCollection Create(ReadOnlySpan items) => null; +} - Assert.True(model.GetConversion(expression).IsBoxing); - } +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } - void assertAttributeData(string name) - { - var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); - Assert.True(attributeData1.HasErrors); + static void Test(params MyCollection a) + { + foreach (var x in a) + { + long y = x; + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var c1Arg = attributeData1.ConstructorArguments.Single(); - Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); - Assert.Equal("MyCollection", c1Arg.Type.ToTestDisplayString()); - Assert.Null(c1Arg.Value); - Assert.Throws(() => c1Arg.Values); - } + comp.VerifyDiagnostics( + // (24,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(24, 9), + // (25,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(25, 14), + // (26,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(26, 9), + // (29,22): error CS0225: The params parameter must have a valid collection type + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(29, 22) + ); } - [Theory] - [InlineData("IEnumerable")] - [InlineData("IReadOnlyCollection")] - [InlineData("IReadOnlyList")] - [InlineData("ICollection")] - [InlineData("IList")] - public void ArrayInterfaces(string @interface) + [Fact] + public void ImplementsIEnumerableT_01() { var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq; + +class MyCollection : IEnumerable +{ + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); +} class Program { @@ -1044,20 +1099,15 @@ static void Main() Test(2, 3); } - static void Test(params -""" + - @interface + -""" - a) + static void Test(params MyCollection a) { - var array = a.ToArray(); - if (array.Length == 0) + if (a.Array.Count == 0) { - System.Console.WriteLine(array.Length); + System.Console.WriteLine(a.Array.Count); } else { - System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[^1]); + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } @@ -1067,7 +1117,7 @@ static void Test2() } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); CompileAndVerify( comp, @@ -1080,23 +1130,88 @@ static void Test2() { VerifyParamsAndAttribute(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); }, - expectedOutput: ExpectedOutput(@" + expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 -")).VerifyDiagnostics(); +").VerifyDiagnostics(); } - [Theory] - [InlineData("IEnumerable")] - [InlineData("IReadOnlyCollection")] - [InlineData("IReadOnlyList")] - [InlineData("ICollection")] - [InlineData("IList")] - public void ArrayInterfaces_InAttribute(string @interface) + [Fact] + public void ImplementsIEnumerableT_02() { - var src = @" -using System.Collections.Generic; + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public IEnumerator GetEnumerator() => throw null; + + public void Add(long l) => throw null; + public void Add(string l) => throw null; +} + +class Program +{ + static void Main() + { + Test("2", 3); + Test(["2", 3]); + Test("2"); + Test(["2"]); + Test(3); + Test([3]); + + MyCollection x1 = ["2"]; + MyCollection x2 = [3]; + } + + static void Test(params MyCollection a) + { + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (19,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // Test("2", 3); + Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(19, 19), + // (20,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' + // Test(["2", 3]); + Diagnostic(ErrorCode.ERR_BadArgType, @"[""2"", 3]").WithArguments("1", "collection expressions", "string").WithLocation(20, 14), + // (23,14): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // Test(3); + Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("1", "int", "string").WithLocation(23, 14), + // (24,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' + // Test([3]); + Diagnostic(ErrorCode.ERR_BadArgType, "[3]").WithArguments("1", "collection expressions", "string").WithLocation(24, 14), + // (27,28): error CS0029: Cannot implicitly convert type 'int' to 'string' + // MyCollection x2 = [3]; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(27, 28) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementsIEnumerableT_03_InAttribute(bool asStruct) + { + var src = @" +using System; +using System.Collections; +using System.Collections.Generic; + +" + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); + IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); + + public void Add(long l) {} +} [Test()] class C1; @@ -1109,21 +1224,21 @@ class C3; class Test : System.Attribute { - public Test(params " + @interface + @" a) {} + public Test(params MyCollection a) {} } "; var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); comp.VerifyDiagnostics( - // (4,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + // (14,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type // [Test()] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(4, 2), - // (7,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(14, 2), + // (17,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type // [Test(1)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(7, 2), - // (10,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(17, 2), + // (20,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type // [Test(2, 3)] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(10, 2) + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(20, 2) ); assertAttributeData("C1"); @@ -1157,2853 +1272,4439 @@ void assertAttributeData(string name) var c1Arg = attributeData1.ConstructorArguments.Single(); Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); - Assert.Equal("System.Collections.Generic." + @interface + "", c1Arg.Type.ToTestDisplayString()); + Assert.Equal("MyCollection", c1Arg.Type.ToTestDisplayString()); Assert.Null(c1Arg.Value); Assert.Throws(() => c1Arg.Values); } } [Fact] - public void IEnumerable() + public void ImplementsIEnumerableT_04_MissingConstructor() { var src = """ using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + public MyCollection(int x){} + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); +} class Program { - static void Test(params IEnumerable a) + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) { } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (5,22): error CS0225: The params parameter must have a valid collection type - // static void Test(params IEnumerable a) - Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 22) + comp.VerifyEmitDiagnostics( + // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), + // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), + + // PROTOTYPE(ParamsCollections): Reword to include params collection? + + // (23,22): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "params MyCollection a").WithLocation(23, 22) ); } [Fact] - public void WRN_ParamsArrayInLambdaOnly_01() + public void ImplementsIEnumerableT_05_InaccessibleConstructor() { var src = """ +using System.Collections; using System.Collections.Generic; +class MyCollection : IEnumerable +{ + protected MyCollection(){} + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); +} + class Program { static void Main() { - System.Action> l = (params IEnumerable x) => {}; - l(null); + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - symbolValidator: (m) => - { - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + comp.VerifyEmitDiagnostics( + // (18,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(18, 9), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14), + // (20,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(20, 9), + // (23,22): error CS0122: 'MyCollection.MyCollection()' is inaccessible due to its protection level + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.MyCollection()").WithLocation(23, 22) + ); + } - Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); - }).VerifyDiagnostics( - // (7,72): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. - // System.Action> l = (params IEnumerable x) => {}; - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 72) - ); + [Fact] + public void ImplementsIEnumerableT_06_LessAccessibleConstructor() + { + var src = """ +using System.Collections; +using System.Collections.Generic; - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); +public class MyCollection : IEnumerable +{ + internal MyCollection(){} + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; - var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); - AssertEx.Equal("params System.Collections.Generic.IEnumerable x", parameter.ToTestDisplayString()); - VerifyParams(parameter, isParamCollection: true); + public void Add(long l) => Array.Add(l); +} - var src2 = """ -class Program +public class Program { static void Main() { - System.Action l = (params long[] x) => {}; - l(null); + Test(); + Test(1); + Test(2, 3); + } + + public static void Test(params MyCollection a) + { } } """; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); - - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); - - comp.VerifyDiagnostics( - // (5,36): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' - // System.Action l = (params long[] x) => {}; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 36), - // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. - // System.Action l = (params long[] x) => {}; - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) + comp.VerifyEmitDiagnostics( + // (23,29): error CS9507: Method 'MyCollection.MyCollection()' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.MyCollection()", "Program.Test(params MyCollection)").WithLocation(23, 29) ); - - comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - - tree = comp.SyntaxTrees.Single(); - model = comp.GetSemanticModel(tree); - - parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); - AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); - VerifyParams(parameter, isParamArray: true); - - CompileAndVerify( - comp, - symbolValidator: (m) => - { - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(System.Int64[] x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); - }).VerifyDiagnostics( - // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. - // System.Action l = (params long[] x) => {}; - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) - ); } [Fact] - public void WRN_ParamsArrayInLambdaOnly_02() + public void ImplementsIEnumerableT_07_MissingAdd() { - // public delegate void D1([ParamArrayAttribute] IEnumerable args); - // public delegate void D2([ParamCollectionAttribute] int[] args); - var il = @" -.class public auto ansi sealed D1 - extends [mscorlib]System.MulticastDelegate + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable { - // Methods - .method public hidebysig specialname rtspecialname - instance void .ctor ( - object 'object', - native int 'method' - ) runtime managed + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +class Program +{ + static void Main() { + Test(); + Test(1); + Test(2, 3); } - .method public hidebysig newslot virtual - instance void Invoke ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 args - ) runtime managed + static void Test(params MyCollection a) { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) } } - -.class public auto ansi sealed D2 - extends [mscorlib]System.MulticastDelegate +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (15,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(15, 9), + // (16,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(16, 14), + // (17,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(17, 9), + + // PROTOTYPE(ParamsCollections): The error looks somewhat confusing. It can be interpreted as though an addition of an extension method might help. + + // (20,22): 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?) + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "params MyCollection a").WithArguments("MyCollection", "Add").WithLocation(20, 22) + ); + } + + [Fact] + public void ImplementsIEnumerableT_08_InaccessibleAdd() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable { - // Methods - .method public hidebysig specialname rtspecialname - instance void .ctor ( - object 'object', - native int 'method' - ) runtime managed - { - } + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; - .method public hidebysig newslot virtual - instance void Invoke ( - int32[] args - ) runtime managed - { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - } + protected void Add(long l) => Array.Add(l); } -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute +class Program { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + static void Main() { - .maxstack 8 + Test(); + Test(1); + Test(2, 3); + } - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + static void Test(params MyCollection a) + { } } -"; +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (17,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(17, 9), + // (18,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(18, 14), + // (19,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(19, 9), + // (22,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(22, 22) + ); + } + [Fact] + public void ImplementsIEnumerableT_09_LessAccessibleAdd() + { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +public class MyCollection : IEnumerable +{ + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + internal void Add(long l) => Array.Add(l); +} + +public class Program { static void Main() { - D1 l1 = (params IEnumerable x) => {}; - D2 l2 = (params int[] x) => {}; - l1 = (IEnumerable x) => {}; - l2 = (int[] x) => {}; - l1(null); - l2(null); + Test(); + Test(1); + Test(2, 3); + } + + public static void Test(params MyCollection a) + { } } """; - CreateCompilationWithIL(src, il).VerifyEmitDiagnostics(); + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + comp.VerifyEmitDiagnostics( + // (22,29): error CS9507: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(22, 29) + ); } [Fact] - public void WRN_ParamsArrayInLambdaOnly_03() + public void ImplementsIEnumerableT_10_MissingAdd_Dynamic() { var src = """ +using System.Collections; using System.Collections.Generic; -public delegate void D1(params IEnumerable args); -public delegate void D2(params int[] args); +class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public class Enumerator + { + public bool MoveNext() => throw null; + public dynamic Current => null; + } +} class Program { static void Main() { - D1 l1 = (params IEnumerable x) => {}; - D2 l2 = (params int[] x) => {}; - l1 = (IEnumerable x) => {}; - l2 = (int[] x) => {}; - l1(null); - l2(null); + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { } } """; - CreateCompilation(src).VerifyEmitDiagnostics(); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (20,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(20, 9), + // (21,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(21, 14), + // (22,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(22, 9), + // (25,22): 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?) + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "params MyCollection a").WithArguments("MyCollection", "Add").WithLocation(25, 22) + ); } [Fact] - public void ParamCollectionInLocalFunctionOnly() + public void ImplementsIEnumerableT_11_InaccessibleAdd_Dynamic() { var src = """ +using System.Collections; using System.Collections.Generic; +class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + protected void Add(long x){} + + public class Enumerator + { + public bool MoveNext() => throw null; + public dynamic Current => null; + } +} + class Program { static void Main() { - void local (params IEnumerable x) {}; - local(1); + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - symbolValidator: (m) => - { - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); - AssertEx.Equal("void Program.
g__local|0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + comp.VerifyEmitDiagnostics( + // (22,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(22, 9), + // (23,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(23, 14), + // (24,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(24, 9), + // (27,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(27, 22) + ); + } - Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); - }).VerifyDiagnostics(); + [Fact] + public void ImplementsIEnumerableT_12_InaccessibleAdd_Dynamic() + { + var src = """ +using System.Collections; +using System.Collections.Generic; - var tree = comp.SyntaxTrees.Single(); - var model = comp.GetSemanticModel(tree); +class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; - var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); - AssertEx.Equal("params System.Collections.Generic.IEnumerable x", parameter.ToTestDisplayString()); - VerifyParams(parameter, isParamCollection: true); + protected void Add(long x){} + protected void Add(int x){} + + public class Enumerator + { + public bool MoveNext() => throw null; + public dynamic Current => null; + } +} - var src2 = """ class Program { static void Main() { - void local (params long[] x) {}; - local(1); + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { } } """; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics( + // (23,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(23, 9), + // (24,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(24, 14), + // (25,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(25, 9), + // (28,22): error CS0122: 'MyCollection.Add(long)' is inaccessible due to its protection level + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_BadAccess, "params MyCollection a").WithArguments("MyCollection.Add(long)").WithLocation(28, 22) + ); + } - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); + [Fact] + public void ImplementsIEnumerableT_13_LessAccessibleAdd_Dynamic() + { + var src = """ +using System.Collections; - comp.VerifyDiagnostics( - // (5,21): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' - // void local (params long[] x) {}; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params long[] x").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 21) - ); +public class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; - comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + internal void Add(long x){} - tree = comp.SyntaxTrees.Single(); - model = comp.GetSemanticModel(tree); + public class Enumerator + { + public bool MoveNext() => throw null; + public dynamic Current => null; + } +} - parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); - AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); - VerifyParams(parameter, isParamArray: true); +public class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } - CompileAndVerify( - comp, - symbolValidator: (m) => - { - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); - AssertEx.Equal("void Program.
g__local|0_0(System.Int64[] x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); - }).VerifyDiagnostics(); + public static void Test(params MyCollection a) + { + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (26,29): error CS9507: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(26, 29) + ); } - [Theory] - [InlineData(@"$""Literal{1}""")] - [InlineData(@"$""Literal"" + $""{1}""")] - public void ConversionInParamsArguments_InterpolatedStringHandler(string expression) + [Fact] + public void ImplementsIEnumerableT_14_LessAccessibleAdd_Dynamic() { - var code = @" -using System; -using System.Linq; - -M(" + expression + ", " + expression + @"); + var src = """ +using System.Collections; +using System.Collections.Generic; -void M(params System.ReadOnlySpan handlers) +public class MyCollection : IEnumerable { - Console.WriteLine(string.Join(Environment.NewLine, handlers.ToArray().Select(h => h.ToString()))); + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + protected void Add(long x){} + internal void Add(int x){} + + public class Enumerator + { + public bool MoveNext() => throw null; + public dynamic Current => null; + } } -"; - var verifier = CompileAndVerify(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false, includeOneTimeHelpers: false) }, targetFramework: TargetFramework.Net80, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsReadOnlySpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x11 }" } - : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -literal:Literal -value:1 -alignment:0 -format: - -literal:Literal -value:1 -alignment:0 -format: -")); - - verifier.VerifyDiagnostics(); - verifier.VerifyIL("", @" +public class Program { - // Code size 122 (0x7a) - .maxstack 5 - .locals init (<>y__InlineArray2 V_0, - CustomHandler V_1) - IL_0000: ldloca.s V_0 - IL_0002: initobj ""<>y__InlineArray2"" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: call ""ref CustomHandler .InlineArrayElementRef<<>y__InlineArray2, CustomHandler>(ref <>y__InlineArray2, int)"" - IL_0010: ldloca.s V_1 - IL_0012: ldc.i4.7 - IL_0013: ldc.i4.1 - IL_0014: call ""CustomHandler..ctor(int, int)"" - IL_0019: ldloca.s V_1 - IL_001b: ldstr ""Literal"" - IL_0020: call ""void CustomHandler.AppendLiteral(string)"" - IL_0025: ldloca.s V_1 - IL_0027: ldc.i4.1 - IL_0028: box ""int"" - IL_002d: ldc.i4.0 - IL_002e: ldnull - IL_002f: call ""void CustomHandler.AppendFormatted(object, int, string)"" - IL_0034: ldloc.1 - IL_0035: stobj ""CustomHandler"" - IL_003a: ldloca.s V_0 - IL_003c: ldc.i4.1 - IL_003d: call ""ref CustomHandler .InlineArrayElementRef<<>y__InlineArray2, CustomHandler>(ref <>y__InlineArray2, int)"" - IL_0042: ldloca.s V_1 - IL_0044: ldc.i4.7 - IL_0045: ldc.i4.1 - IL_0046: call ""CustomHandler..ctor(int, int)"" - IL_004b: ldloca.s V_1 - IL_004d: ldstr ""Literal"" - IL_0052: call ""void CustomHandler.AppendLiteral(string)"" - IL_0057: ldloca.s V_1 - IL_0059: ldc.i4.1 - IL_005a: box ""int"" - IL_005f: ldc.i4.0 - IL_0060: ldnull - IL_0061: call ""void CustomHandler.AppendFormatted(object, int, string)"" - IL_0066: ldloc.1 - IL_0067: stobj ""CustomHandler"" - IL_006c: ldloca.s V_0 - IL_006e: ldc.i4.2 - IL_006f: call ""System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray2, CustomHandler>(in <>y__InlineArray2, int)"" - IL_0074: call ""void Program.<
$>g__M|0_0(System.ReadOnlySpan)"" - IL_0079: ret + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + public static void Test(params MyCollection a) + { + } } -"); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (28,29): error CS9507: Method 'MyCollection.Add(int)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(int)", "Program.Test(params MyCollection)").WithLocation(28, 29) + ); } [Fact] - public void OrderOfEvaluation_01_NamedArguments() + public void ImplementsIEnumerableT_15_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; -using System.Collections.Generic; -class MyCollection : IEnumerable +public class MyCollection : IEnumerable { - public MyCollection() - { - System.Console.WriteLine("Create"); - } - - IEnumerator IEnumerable.GetEnumerator() => throw null; + public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; - public void Add(int l) + internal void Add(long x){} + internal void Add(int x){} + + public class Enumerator { - System.Console.WriteLine("Add"); + public bool MoveNext() => throw null; + public dynamic Current => null; } } -class Program +public class Program { static void Main() { - Test(b: GetB(), c: GetC(), a: GetA()); + Test(); + Test(1); + Test(2, 3); } - static void Test(int a, int b, params MyCollection c) + public static void Test(params MyCollection a) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - static int GetA() + comp.VerifyEmitDiagnostics( + // (27,29): error CS9507: Method 'MyCollection.Add(long)' cannot be less visible than the member with params collection 'Program.Test(params MyCollection)'. + // public static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection a").WithArguments("MyCollection.Add(long)", "Program.Test(params MyCollection)").WithLocation(27, 29) + ); + } + + [Fact] + public void ImplementsIEnumerableT_16_LessAccessibleAdd_Dynamic() + { + var src = """ +using System.Collections; + +public class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long x){} + internal void Add(int x){} + + public class Enumerator { - System.Console.WriteLine("GetA"); - return 0; + public bool MoveNext() => throw null; + public dynamic Current => null; } +} - static int GetB() +public class Program +{ + static void Main() { - System.Console.WriteLine("GetB"); - return 0; + Test(); + Test(1); + Test(2, 3); } - static int GetC() + public static void Test(params MyCollection a) { - System.Console.WriteLine("GetC"); - return 0; } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - - var verifier = CompileAndVerify( - comp, - expectedOutput: @" -GetB -Create -GetC -Add -GetA -").VerifyDiagnostics(); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - // Note, the collection is created after the lexically previous argument is evaluated, - // but before the lexically following argument is evaluated. This differs from params - // array case, which is created right before the target methos is invoked, after all - // arguments are evaluated in their lexical order, which can be observed in a unit-test - // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.CodeGenTests.NamedParamsOptimizationAndParams002​ - verifier.VerifyIL("Program.Main", @" -{ - // Code size 36 (0x24) - .maxstack 3 - .locals init (int V_0, - MyCollection V_1) - IL_0000: call ""int Program.GetB()"" - IL_0005: stloc.0 - IL_0006: newobj ""MyCollection..ctor()"" - IL_000b: dup - IL_000c: call ""int Program.GetC()"" - IL_0011: callvirt ""void MyCollection.Add(int)"" - IL_0016: stloc.1 - IL_0017: call ""int Program.GetA()"" - IL_001c: ldloc.0 - IL_001d: ldloc.1 - IL_001e: call ""void Program.Test(int, int, params MyCollection)"" - IL_0023: ret -} -"); + comp.VerifyEmitDiagnostics(); } [Fact] - public void OrderOfEvaluation_02_CompoundAssignment() + public void ImplementsIEnumerableT_17_LessAccessibleAdd_Dynamic() { var src = """ using System.Collections; -using System.Collections.Generic; -class MyCollection : IEnumerable +public class MyCollection : IEnumerable { - public MyCollection() - { - System.Console.WriteLine("Create"); - } - - IEnumerator IEnumerable.GetEnumerator() => throw null; + public Enumerator GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; - public void Add(int l) + internal void Add(long x){} + public void Add(int x){} + + public class Enumerator { - System.Console.WriteLine("Add"); + public bool MoveNext() => throw null; + public dynamic Current => null; } } -class Program +public class Program { - private MyCollection _c; - static void Main() { - System.Console.WriteLine("---Test1"); - Test1(new Program()); - System.Console.WriteLine("---Test2"); - Test2(new Program()); - System.Console.WriteLine("---Test3"); - Test3(new Program()); + Test(); + Test(1); + Test(2, 3); } - static void Test1(Program p) + public static void Test(params MyCollection a) { - p[GetA()]++; } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - static void Test2(Program p) + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ImplementsIEnumerableT_18_LessAccessibleAdd_Dynamic() + { + var src = """ +using System.Collections; + +public class MyCollection : IEnumerable +{ + public Enumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + internal void Add(long x){} + public void Add(int x){} + internal void Add(byte x){} + + public class Enumerator { - p[GetA(), GetC()]++; + public bool MoveNext() => throw null; + public dynamic Current => null; } +} - static void Test3(Program p) +public class Program +{ + static void Main() { - p[GetA(), GetB(), GetC()]++; + Test(); + Test(1); + Test(2, 3); } - int this[int a, params MyCollection c] + public static void Test(params MyCollection a) { - get - { - System.Console.WriteLine("Get_this {0}", c is not null); - _c = c; - return 0; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics(); } - set + + [Fact] + public void ImplementsIEnumerableT_19_NoElementType() { - System.Console.WriteLine("Set_this {0}", (object)_c == c); - } - } + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable, IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(long l) => throw null; +} - static int GetA() +class Program +{ + static void Main() { - System.Console.WriteLine("GetA"); - return 0; + Test(); + Test(1); + Test(2, 3); } - static int GetB() + static void Test(params MyCollection a) { - System.Console.WriteLine("GetB"); - return 0; - } - - static int GetC() - { - System.Console.WriteLine("GetC"); - return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify( - comp, - expectedOutput: @" ----Test1 -GetA -Create -Get_this True -Set_this True ----Test2 -GetA -Create -GetC -Add -Get_this True -Set_this True ----Test3 -GetA -Create -GetB -Add -GetC -Add -Get_this True -Set_this True -").VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (17,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'Program.Test(params MyCollection)' + // Test(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Test").WithArguments("a", "Program.Test(params MyCollection)").WithLocation(17, 9), + // (18,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params MyCollection").WithLocation(18, 14), + // (19,9): error CS1501: No overload for method 'Test' takes 2 arguments + // Test(2, 3); + Diagnostic(ErrorCode.ERR_BadArgCount, "Test").WithArguments("Test", "2").WithLocation(19, 9), + // (22,22): error CS0225: The params parameter must have a valid collection type + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(22, 22) + ); + } - verifier.VerifyIL("Program.Test1", @" + [Fact] + public void ImplementsIEnumerableT_20_LessAccessibleConstructorAndAdd_NoError_In_LambdaOrLocalFunction() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +public class MyCollection : IEnumerable { - // Code size 33 (0x21) - .maxstack 5 - .locals init (int V_0, - MyCollection V_1, - int V_2) - IL_0000: ldarg.0 - IL_0001: call ""int Program.GetA()"" - IL_0006: stloc.0 - IL_0007: newobj ""MyCollection..ctor()"" - IL_000c: stloc.1 - IL_000d: dup - IL_000e: ldloc.0 - IL_000f: ldloc.1 - IL_0010: callvirt ""int Program.this[int, params MyCollection].get"" - IL_0015: stloc.2 - IL_0016: ldloc.0 - IL_0017: ldloc.1 - IL_0018: ldloc.2 - IL_0019: ldc.i4.1 - IL_001a: add - IL_001b: callvirt ""void Program.this[int, params MyCollection].set"" - IL_0020: ret + internal MyCollection() { } + internal void Add(string s) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; } -"); - verifier.VerifyIL("Program.Test2", @" +public class Program { - // Code size 44 (0x2c) - .maxstack 5 - .locals init (int V_0, - MyCollection V_1, - int V_2) - IL_0000: ldarg.0 - IL_0001: call ""int Program.GetA()"" - IL_0006: stloc.0 - IL_0007: newobj ""MyCollection..ctor()"" - IL_000c: dup - IL_000d: call ""int Program.GetC()"" - IL_0012: callvirt ""void MyCollection.Add(int)"" - IL_0017: stloc.1 - IL_0018: dup - IL_0019: ldloc.0 - IL_001a: ldloc.1 - IL_001b: callvirt ""int Program.this[int, params MyCollection].get"" - IL_0020: stloc.2 - IL_0021: ldloc.0 - IL_0022: ldloc.1 - IL_0023: ldloc.2 - IL_0024: ldc.i4.1 - IL_0025: add - IL_0026: callvirt ""void Program.this[int, params MyCollection].set"" - IL_002b: ret -} -"); + public void Test1() + { + local(); + local("a"); - verifier.VerifyIL("Program.Test3", @" -{ - // Code size 55 (0x37) - .maxstack 5 - .locals init (int V_0, - MyCollection V_1, - int V_2) - IL_0000: ldarg.0 - IL_0001: call ""int Program.GetA()"" - IL_0006: stloc.0 - IL_0007: newobj ""MyCollection..ctor()"" - IL_000c: dup - IL_000d: call ""int Program.GetB()"" - IL_0012: callvirt ""void MyCollection.Add(int)"" - IL_0017: dup - IL_0018: call ""int Program.GetC()"" - IL_001d: callvirt ""void MyCollection.Add(int)"" - IL_0022: stloc.1 - IL_0023: dup - IL_0024: ldloc.0 - IL_0025: ldloc.1 - IL_0026: callvirt ""int Program.this[int, params MyCollection].get"" - IL_002b: stloc.2 - IL_002c: ldloc.0 - IL_002d: ldloc.1 - IL_002e: ldloc.2 - IL_002f: ldc.i4.1 - IL_0030: add - IL_0031: callvirt ""void Program.this[int, params MyCollection].set"" - IL_0036: ret + void local(params MyCollection collection) { } + + var x = (params MyCollection collection) => { }; + } + + public void Test2(params MyCollection collection) + { + } } -"); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + comp.VerifyEmitDiagnostics( + // (25,23): error CS9507: Method 'MyCollection.MyCollection()' cannot be less visible than the member with params collection 'Program.Test2(params MyCollection)'. + // public void Test2(params MyCollection collection) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection collection").WithArguments("MyCollection.MyCollection()", "Program.Test2(params MyCollection)").WithLocation(25, 23), + // (25,23): error CS9507: Method 'MyCollection.Add(string)' cannot be less visible than the member with params collection 'Program.Test2(params MyCollection)'. + // public void Test2(params MyCollection collection) + Diagnostic(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, "params MyCollection collection").WithArguments("MyCollection.Add(string)", "Program.Test2(params MyCollection)").WithLocation(25, 23) + ); } [Fact] - public void OrderOfEvaluation_03_ObjectInitializer() + public void ImplementsIEnumerableT_21_AddIsNotAnExtension() { var src = """ using System.Collections; using System.Collections.Generic; -class MyCollection : IEnumerable +class MyCollection : IEnumerable { - public MyCollection() - { - System.Console.WriteLine("Create"); - } - - IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; - - public void Add(int l) - { - System.Console.WriteLine("Add"); - } } -class C1 +static class Ext { - public int F1; - public int F2; + public static void Add(this MyCollection c, long l) {} } class Program { - private MyCollection _c; - static void Main() { - System.Console.WriteLine("---Test1"); - Test1(); - System.Console.WriteLine("---Test2"); - Test2(); - System.Console.WriteLine("---Test3"); - Test3(); + Test(); + Test(1); + Test(2, 3); } - static void Test1() + static void Test(params MyCollection a) { - _ = new Program() { [GetA()] = { F1 = GetF1(), F2 = GetF2() } }; } static void Test2() { - _ = new Program() { [GetA(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; - } - - static void Test3() - { - _ = new Program() { [GetA(), GetB(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; + Test([2, 3]); } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - C1 this[int a, params MyCollection c] - { - get - { - System.Console.WriteLine("Get_this {0}", c is not null && (_c is null || (object)_c == c)); - _c = c; - return new C1(); - } - set - { - System.Console.WriteLine("Set_this {0}", (object)_c == c); + // PROTOTYPE(ParamsCollections): Report more specific error saying that extension methods are ignored? + comp.VerifyDiagnostics( + // (24,22): error CS0117: 'MyCollection' does not contain a definition for 'Add' + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_NoSuchMember, "params MyCollection a").WithArguments("MyCollection", "Add").WithLocation(24, 22) + ); } - } + [Fact] + public void ImplementsIEnumerable_01() + { + var src = """ +using System.Collections; +using System.Collections.Generic; - static int GetA() - { - System.Console.WriteLine("GetA"); - return 0; - } +class MyCollection : IEnumerable +{ + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; - static int GetB() - { - System.Console.WriteLine("GetB"); - return 0; - } + public void Add(object l) => Array.Add(l); +} - static int GetC() +class Program +{ + static void Main() { - System.Console.WriteLine("GetC"); - return 0; + Test(); + Test(1); + Test(2, 3); } - static int GetF1() + static void Test(params MyCollection a) { - System.Console.WriteLine("GetF1"); - return 0; + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } } - static int GetF2() + static void Test2() { - System.Console.WriteLine("GetF2"); - return 0; + Test([2, 3]); } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - var verifier = CompileAndVerify( + CompileAndVerify( comp, + sourceSymbolValidator: static (m) => + { + VerifyParams(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); + }, + symbolValidator: static (m) => + { + VerifyParamsAndAttribute(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); + }, expectedOutput: @" ----Test1 -GetA -Create -Get_this True -GetF1 -Get_this True -GetF2 ----Test2 -GetA -Create -GetC -Add -Get_this True -GetF1 -Get_this True -GetF2 ----Test3 -GetA -Create -GetB -Add -GetC -Add -Get_this True -GetF1 -Get_this True -GetF2 +0 +1: 1 ... 1 +2: 2 ... 3 ").VerifyDiagnostics(); + } - // Note, the collection is created once and that same instance is used across multiple invocation of the indexer. - // With params arrays, however, only individual elements are cached and each invocation of the indexer is getting - // a new instance of an array (with the same values inside though). This can be observed in a unit-test - // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.ObjectAndCollectionInitializerTests.DictionaryInitializerTestSideeffects001param - verifier.VerifyIL("Program.Test1", @" -{ - // Code size 53 (0x35) - .maxstack 4 - .locals init (int V_0, - MyCollection V_1) - IL_0000: newobj ""Program..ctor()"" - IL_0005: call ""int Program.GetA()"" - IL_000a: stloc.0 - IL_000b: newobj ""MyCollection..ctor()"" - IL_0010: stloc.1 - IL_0011: dup - IL_0012: ldloc.0 - IL_0013: ldloc.1 - IL_0014: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_0019: call ""int Program.GetF1()"" - IL_001e: stfld ""int C1.F1"" - IL_0023: ldloc.0 - IL_0024: ldloc.1 - IL_0025: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_002a: call ""int Program.GetF2()"" - IL_002f: stfld ""int C1.F2"" - IL_0034: ret -} -"); + [Fact] + public void ImplementsIEnumerable_02() + { + var src = """ +using System.Collections; +using System.Collections.Generic; - verifier.VerifyIL("Program.Test2", @" +class MyCollection : IEnumerable { - // Code size 64 (0x40) - .maxstack 4 - .locals init (int V_0, - MyCollection V_1) - IL_0000: newobj ""Program..ctor()"" - IL_0005: call ""int Program.GetA()"" - IL_000a: stloc.0 - IL_000b: newobj ""MyCollection..ctor()"" - IL_0010: dup - IL_0011: call ""int Program.GetC()"" - IL_0016: callvirt ""void MyCollection.Add(int)"" - IL_001b: stloc.1 - IL_001c: dup - IL_001d: ldloc.0 - IL_001e: ldloc.1 - IL_001f: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_0024: call ""int Program.GetF1()"" - IL_0029: stfld ""int C1.F1"" - IL_002e: ldloc.0 - IL_002f: ldloc.1 - IL_0030: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_0035: call ""int Program.GetF2()"" - IL_003a: stfld ""int C1.F2"" - IL_003f: ret -} -"); + IEnumerator IEnumerable.GetEnumerator() => throw null; - verifier.VerifyIL("Program.Test3", @" -{ - // Code size 75 (0x4b) - .maxstack 4 - .locals init (int V_0, - MyCollection V_1) - IL_0000: newobj ""Program..ctor()"" - IL_0005: call ""int Program.GetA()"" - IL_000a: stloc.0 - IL_000b: newobj ""MyCollection..ctor()"" - IL_0010: dup - IL_0011: call ""int Program.GetB()"" - IL_0016: callvirt ""void MyCollection.Add(int)"" - IL_001b: dup - IL_001c: call ""int Program.GetC()"" - IL_0021: callvirt ""void MyCollection.Add(int)"" - IL_0026: stloc.1 - IL_0027: dup - IL_0028: ldloc.0 - IL_0029: ldloc.1 - IL_002a: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_002f: call ""int Program.GetF1()"" - IL_0034: stfld ""int C1.F1"" - IL_0039: ldloc.0 - IL_003a: ldloc.1 - IL_003b: callvirt ""C1 Program.this[int, params MyCollection].get"" - IL_0040: call ""int Program.GetF2()"" - IL_0045: stfld ""int C1.F2"" - IL_004a: ret + public IEnumerator GetEnumerator() => throw null; + public void Add(object l) => throw null; } -"); - } - [Fact] - public void LanguageVersion_01_Declaration() - { - var src = @" class Program { - static void Test1(params System.ReadOnlySpan a) {} - static void Test2(params long[] a) {} + static void Main() + { + Test("2", 3); + Test(["2", 3]); + } - void Test() + static void Test(params MyCollection a) { - Test1(1); - Test2(2); } } -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); - - comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp.VerifyDiagnostics(); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( - // (4,23): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // static void Test1(params System.ReadOnlySpan a) {} - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(4, 23) + // (16,19): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // Test("2", 3); + Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(16, 19), + // (17,14): error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string' + // Test(["2", 3]); + Diagnostic(ErrorCode.ERR_BadArgType, @"[""2"", 3]").WithArguments("1", "collection expressions", "string").WithLocation(17, 14) ); } - [Fact] - public void LanguageVersion_02_CallSite() + [Theory] + [CombinatorialData] + public void ImplementsIEnumerable_03_InAttribute(bool asStruct) { - var src1 = @" -public class Params -{ - static public void Test1(params System.ReadOnlySpan a) {} - static public void Test2(params long[] a) {} -} -"; - var src2 = @" -class Program -{ - void Test() - { - Params.Test1(1); - Params.Test2(2); + var src = @" +using System; +using System.Collections; - Params.Test1(); - Params.Test2(); +" + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); - Params.Test1([1]); - Params.Test2([2]); - } + public void Add(object l) {} } -"; - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - - verify(comp1.ToMetadataReference()); - verify(comp1.EmitToImageReference()); - void verify(MetadataReference comp1Ref) - { - var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp2.VerifyDiagnostics(); +[Test()] +class C1; - comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp2.VerifyDiagnostics(); +[Test(1)] +class C2; - comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); - comp2.VerifyDiagnostics( - // (6,9): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // Params.Test1(1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1(1)").WithArguments("params collections").WithLocation(6, 9), - // (9,9): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // Params.Test1(); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1()").WithArguments("params collections").WithLocation(9, 9) - ); - } - } +[Test(2, 3)] +class C3; - [Fact] - public void LanguageVersion_03_DelegateNaturalType() - { - var src1 = @" -public class Params +class Test : System.Attribute { - static public void Test1(params System.ReadOnlySpan a) {} - static public void Test2(params long[] a) {} + public Test(params MyCollection a) {} } "; - var src2 = @" -class Program -{ - void Test() - { - var x1 = Params.Test1; - var x2 = Params.Test2; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - x1(1); - x2(2); + comp.VerifyDiagnostics( + // (12,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type + // [Test()] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(12, 2), + // (15,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type + // [Test(1)] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(15, 2), + // (18,2): error CS0181: Attribute constructor parameter 'a' has type 'MyCollection', which is not a valid attribute parameter type + // [Test(2, 3)] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "MyCollection").WithLocation(18, 2) + ); - x1(); - x2(); + assertAttributeData("C1"); + assertAttributeData("C2"); + assertAttributeData("C3"); - x1([1]); - x2([2]); - } -} -"; - var comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); + var tree = comp.SyntaxTrees.Single(); + var nodes = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(3, nodes.Length); - comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp.VerifyDiagnostics(); + var model = comp.GetSemanticModel(tree); - comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); - comp.VerifyDiagnostics( - // (22,30): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // static public void Test1(params System.ReadOnlySpan a) {} - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(22, 30) - ); + foreach (LiteralExpressionSyntax expression in nodes) + { + assertTypeInfo(expression); + } - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + void assertTypeInfo(LiteralExpressionSyntax expression) + { + var typeInfo = model.GetTypeInfo(expression); + Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Object", typeInfo.ConvertedType.ToTestDisplayString()); - verify(comp1.ToMetadataReference()); - verify(comp1.EmitToImageReference()); + Assert.True(model.GetConversion(expression).IsBoxing); + } - void verify(MetadataReference comp1Ref) + void assertAttributeData(string name) { - var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp2.VerifyDiagnostics(); - - comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp2.VerifyDiagnostics(); + var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); + Assert.True(attributeData1.HasErrors); - comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); - comp2.VerifyDiagnostics( - // (6,18): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // var x1 = Params.Test1; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(6, 18) - ); + var c1Arg = attributeData1.ConstructorArguments.Single(); + Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); + Assert.Equal("MyCollection", c1Arg.Type.ToTestDisplayString()); + Assert.Null(c1Arg.Value); + Assert.Throws(() => c1Arg.Values); } } - [Fact] - public void LanguageVersion_04_DelegateNaturalType() + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + [InlineData("ICollection")] + [InlineData("IList")] + public void ArrayInterfaces(string @interface) { - var src = @" + var src = """ +using System.Collections.Generic; +using System.Linq; + class Program { - void Test() + static void Main() { - var x1 = (params System.ReadOnlySpan a) => {}; - var x2 = (params long[] a) => {}; - - x1(1); - x2(2); - - x1(); - x2(); - - x1([1]); - x2([2]); - - M1(x1); - M1(x2); + Test(); + Test(1); + Test(2, 3); + } - M1((params System.ReadOnlySpan b) => {}); - M1((params long[] b) => {}); + static void Test(params +""" + + @interface + +""" + a) + { + var array = a.ToArray(); + if (array.Length == 0) + { + System.Console.WriteLine(array.Length); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[^1]); + } } - static void M1(T t) {} + static void Test2() + { + Test([2, 3]); + } } -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); - - comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp.VerifyDiagnostics(); +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); - comp.VerifyDiagnostics( - // (6,19): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // var x1 = (params System.ReadOnlySpan a) => {}; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(6, 19), - // (21,13): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1((params System.ReadOnlySpan b) => {}); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan b").WithArguments("params collections").WithLocation(21, 13) - ); + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + sourceSymbolValidator: static (m) => + { + VerifyParams(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); + }, + symbolValidator: static (m) => + { + VerifyParamsAndAttribute(m.GlobalNamespace.GetMember("Program.Test").Parameters.Last(), isParamCollection: true); + }, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); } - [Fact] - public void LanguageVersion_05_DelegateNaturalType() + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + [InlineData("ICollection")] + [InlineData("IList")] + public void ArrayInterfaces_InAttribute(string @interface) { - var src1 = @" -public class Params -{ - static public void Test1(params System.Collections.Generic.IEnumerable a) {} - static public void Test2(params long[] a) {} -} -"; - var src2 = @" -class Program -{ - void Test1() - { - var a = Params.Test1; - M1(a); // See DelegateNaturalType_03 unit-test for an observable effect that 'params' modifier has for this invocation. - M1(Params.Test1); - } + var src = @" +using System.Collections.Generic; - static void M1(T t) {} +[Test()] +class C1; - void Test2() - { - var b = Params.Test2; - M1(b); - M1(Params.Test2); - } +[Test(1)] +class C2; + +[Test(2, 3)] +class C3; + +class Test : System.Attribute +{ + public Test(params " + @interface + @" a) {} } "; - var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); - - comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp.VerifyDiagnostics(); + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( - // (23,30): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // static public void Test1(params System.Collections.Generic.IEnumerable a) {} - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.Collections.Generic.IEnumerable a").WithArguments("params collections").WithLocation(23, 30) + // (4,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + // [Test()] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(4, 2), + // (7,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + // [Test(1)] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(7, 2), + // (10,2): error CS0181: Attribute constructor parameter 'a' has type 'ICollection', which is not a valid attribute parameter type + // [Test(2, 3)] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Test").WithArguments("a", "System.Collections.Generic." + @interface + "").WithLocation(10, 2) ); - var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); + assertAttributeData("C1"); + assertAttributeData("C2"); + assertAttributeData("C3"); - verify(comp1.ToMetadataReference()); - verify(comp1.EmitToImageReference()); + var tree = comp.SyntaxTrees.Single(); + var nodes = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(3, nodes.Length); - void verify(MetadataReference comp1Ref) + var model = comp.GetSemanticModel(tree); + + foreach (LiteralExpressionSyntax expression in nodes) { - var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp2.VerifyDiagnostics(); + assertTypeInfo(expression); + } - comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp2.VerifyDiagnostics(); + void assertTypeInfo(LiteralExpressionSyntax expression) + { + var typeInfo = model.GetTypeInfo(expression); + Assert.Equal("System.Int32", typeInfo.Type.ToTestDisplayString()); + Assert.Equal("System.Int64", typeInfo.ConvertedType.ToTestDisplayString()); - comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); - comp2.VerifyDiagnostics( - // (6,17): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // var a = Params.Test1; - Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(6, 17), - // (8,12): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // M1(Params.Test1); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(8, 12) - ); + Assert.True(model.GetConversion(expression).IsNumeric); + } + + void assertAttributeData(string name) + { + var attributeData1 = comp.GetTypeByMetadataName(name).GetAttributes().Single(); + Assert.True(attributeData1.HasErrors); + + var c1Arg = attributeData1.ConstructorArguments.Single(); + Assert.Equal(TypedConstantKind.Error, c1Arg.Kind); + Assert.Equal("System.Collections.Generic." + @interface + "", c1Arg.Type.ToTestDisplayString()); + Assert.Null(c1Arg.Value); + Assert.Throws(() => c1Arg.Values); } } [Fact] - public void LanguageVersion_06_LambdaForDelegateWithParams() + public void IEnumerable() { - var src1 = @" -public class Params -{ - static public void Test1(D1 d) {} - static public void Test2(D2 d) {} -} + var src = """ +using System.Collections; -public delegate void D1(params System.Collections.Generic.IEnumerable a); -public delegate void D2(params long[] a); -"; - var src2 = @" -class Program1 -{ - void Test1() - { - Params.Test1(e1 => { }); - } -} -class Program2 +class Program { - void Test2() + static void Test(params IEnumerable a) { - Params.Test2(e2 => { }); + Test(new object()); } } -"; - var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); - - comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp.VerifyDiagnostics(); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( - // (23,25): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. - // public delegate void D1(params System.Collections.Generic.IEnumerable a); - Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.Collections.Generic.IEnumerable a").WithArguments("params collections").WithLocation(23, 25) + // (5,22): error CS0225: The params parameter must have a valid collection type + // static void Test(params IEnumerable a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 22), + // (7,14): error CS1503: Argument 1: cannot convert from 'object' to 'params System.Collections.IEnumerable' + // Test(new object()); + Diagnostic(ErrorCode.ERR_BadArgType, "new object()").WithArguments("1", "object", "params System.Collections.IEnumerable").WithLocation(7, 14) ); - - var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); - - verify(comp1.ToMetadataReference()); - verify(comp1.EmitToImageReference()); - - void verify(MetadataReference comp1Ref) - { - var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); - comp2.VerifyDiagnostics(); - - comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); - comp2.VerifyDiagnostics(); - - comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular12); - - var tree = comp2.SyntaxTrees.Single(); - var model = comp2.GetSemanticModel(tree); - - var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().First()); - AssertEx.Equal("System.Collections.Generic.IEnumerable e1", parameter.ToTestDisplayString()); - Assert.False(parameter.IsParams); - Assert.False(parameter.IsParamCollection); - - CompileAndVerify(comp2, - symbolValidator: (m) => - { - var lambda = m.GlobalNamespace.GetMember("Program1.<>c.b__0_0"); - ParameterSymbol parameter = lambda.Parameters.Single(); - - VerifyParamsAndAttribute(parameter); - Assert.Equal("System.Collections.Generic.IEnumerable e1", parameter.ToTestDisplayString()); - } - ).VerifyDiagnostics(); // No language version diagnostics as expected. The 'params' modifier doesn't even make it to symbol and metadata. - } } [Fact] - public void DelegateNaturalType_01() + public void WRN_ParamsArrayInLambdaOnly_01() { - var src1 = @" -public class Params -{ - static public void Test1(params System.ReadOnlySpan a) { System.Console.WriteLine(a.Length); } - static public void Test2(params long[] a) { System.Console.WriteLine(a.Length); } -} -"; - var src2 = @" + var src = """ +using System.Collections.Generic; + class Program { static void Main() { - var x1 = Params.Test1; - var x2 = Params.Test2; - - x1(1); - x2(2); - - x1(); - x2(); + System.Action> l = (params IEnumerable x) => {}; + l(null); } } -"; - var comp = CreateCompilation(src1 + src2, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - verify(comp, attributeIsEmbedded: true); - - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - verify(comp2, attributeIsEmbedded: true); - - var comp3 = CreateCompilation(src1 + ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - var comp4 = CreateCompilation(src2, references: [comp3.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - verify(comp4, attributeIsEmbedded: false); - - var comp5 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseModule); - comp5.VerifyDiagnostics(); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); - void verify(CSharpCompilation comp, bool attributeIsEmbedded) - { - // We want to test attribute embedding - Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + CompileAndVerify( + comp, + symbolValidator: (m) => + { + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); + AssertEx.Equal("void Program.<>c.
b__0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); - CompileAndVerify( - comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, - symbolValidator: (m) => - { - MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); + Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + }).VerifyDiagnostics( + // (7,72): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // System.Action> l = (params IEnumerable x) => {}; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(7, 72) + ); - MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); - if (attributeIsEmbedded) - { - Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); - } - else - { - Assert.Empty(m.GlobalNamespace.GetMembers("System")); - } - }, - expectedOutput: ExpectedOutput(@" -1 -1 -0 -0 -")).VerifyDiagnostics(); - } - } + var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); + AssertEx.Equal("params System.Collections.Generic.IEnumerable x", parameter.ToTestDisplayString()); + VerifyParams(parameter, isParamCollection: true); - [Fact] - public void DelegateNaturalType_02() - { - var src = @" + var src2 = """ class Program { static void Main() { - var x1 = (params System.ReadOnlySpan a) => System.Console.WriteLine(a.Length); - var x2 = (params long[] a) => System.Console.WriteLine(a.Length); - - x1(1); - x2(2); - - x1(); - x2(); + System.Action l = (params long[] x) => {}; + l(null); } } -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp, attributeIsEmbedded: true); - - var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp2, attributeIsEmbedded: false); +"""; - void verify(CSharpCompilation comp, bool attributeIsEmbedded) - { - // We want to test attribute embedding - Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, - symbolValidator: (m) => - { - MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); - MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); + comp.VerifyDiagnostics( + // (5,36): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // System.Action l = (params long[] x) => {}; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 36), + // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // System.Action l = (params long[] x) => {}; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) + ); - // Note, no attributes on lambdas + comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(System.ReadOnlySpan a)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + tree = comp.SyntaxTrees.Single(); + model = comp.GetSemanticModel(tree); - MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_1"); - AssertEx.Equal("void Program.<>c.
b__0_1(System.Int64[] a)", l2.ToTestDisplayString()); - VerifyParamsAndAttribute(l2.Parameters.Last()); + parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); + AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); + VerifyParams(parameter, isParamArray: true); - if (attributeIsEmbedded) - { - Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); - } - else - { - Assert.Empty(m.GlobalNamespace.GetMembers("System")); - } - }, - expectedOutput: ExpectedOutput(@" -1 -1 -0 -0 -")).VerifyDiagnostics(); - } + CompileAndVerify( + comp, + symbolValidator: (m) => + { + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); + AssertEx.Equal("void Program.<>c.
b__0_0(System.Int64[] x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); + }).VerifyDiagnostics( + // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. + // System.Action l = (params long[] x) => {}; + Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(5, 50) + ); } [Fact] - public void DelegateNaturalType_03() + public void WRN_ParamsArrayInLambdaOnly_02() { - var src = @" -class Program + // public delegate void D1([ParamArrayAttribute] IEnumerable args); + // public delegate void D2([ParamCollectionAttribute] int[] args); + var il = @" +.class public auto ansi sealed D1 + extends [mscorlib]System.MulticastDelegate { - static public void Test1(System.Collections.Generic.IEnumerable a) { System.Console.WriteLine("" {0}"", a is not null); } - static public void Test2(params System.Collections.Generic.IEnumerable a) { System.Console.WriteLine("" {0}"", a is not null); } - static public void Test3(params System.Collections.Generic.List a) { System.Console.WriteLine("" {0}"", a is not null); } - static public void Test4(params long[] a) { System.Console.WriteLine("" {0}"", a is not null); } - - static void Main() + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + object 'object', + native int 'method' + ) runtime managed { - DoTest1(); - DoTest21(); - DoTest22(); - DoTest3(); - DoTest4(); } - static void DoTest1() + .method public hidebysig newslot virtual + instance void Invoke ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 args + ) runtime managed { - var a1 = Test1; - M(a1); + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) } +} - static void DoTest21() +.class public auto ansi sealed D2 + extends [mscorlib]System.MulticastDelegate +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + object 'object', + native int 'method' + ) runtime managed { - var a2 = Test2; - M(a2)(); } - static void DoTest22() + .method public hidebysig newslot virtual + instance void Invoke ( + int32[] args + ) runtime managed { - var a2 = Test2; - M(a2)(); + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) } +} - static void DoTest3() +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - var a3 = Test3; - M(a3)(); - } + .maxstack 8 - static void DoTest4() - { - var a4 = Test4; - M(a4)(); + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } - - static T M(T t) { System.Console.WriteLine(typeof(T)); return t; } - static void M(System.Action> t) => System.Console.WriteLine(""Action""); - static void M(System.Action> t) => System.Console.WriteLine(""Action""); } "; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify( - comp, - symbolValidator: (m) => - { - AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod.ToTestDisplayString()); - AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params System.Collections.Generic.List arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1").DelegateInvokeMethod.ToTestDisplayString()); - AssertEx.Equal("void <>f__AnonymousDelegate2.Invoke(params T1[] arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate2`1").DelegateInvokeMethod.ToTestDisplayString()); - }, - expectedOutput: ExpectedOutput(@" -Action -<>f__AnonymousDelegate0 - True -<>f__AnonymousDelegate0 - True -<>f__AnonymousDelegate1 - True -<>f__AnonymousDelegate2`1[System.Int64] - True -")).VerifyDiagnostics(); - } + var src = """ +using System.Collections.Generic; - [Fact] - public void DelegateNaturalType_04() - { - var src = @" class Program { static void Main() { - var x1 = Test1; - var x2 = Test2; - - x1(1); - x2(2); - - x1(); - x2(); - - static void Test1(params System.ReadOnlySpan a) { System.Console.WriteLine(a.Length); } - static void Test2(params long[] a) { System.Console.WriteLine(a.Length); } + D1 l1 = (params IEnumerable x) => {}; + D2 l2 = (params int[] x) => {}; + l1 = (IEnumerable x) => {}; + l2 = (int[] x) => {}; + l1(null); + l2(null); } } -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp, attributeIsEmbedded: true); - - var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp2, attributeIsEmbedded: false); - - void verify(CSharpCompilation comp, bool attributeIsEmbedded) - { - // We want to test attribute embedding - Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); - - CompileAndVerify( - comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, - symbolValidator: (m) => - { - MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); - - MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; - AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); - VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); - - // Note, no attributes on local functions - - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__Test1|0_0"); - AssertEx.Equal("void Program.
g__Test1|0_0(System.ReadOnlySpan a)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); - - MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.
g__Test2|0_1"); - AssertEx.Equal("void Program.
g__Test2|0_1(System.Int64[] a)", l2.ToTestDisplayString()); - VerifyParamsAndAttribute(l2.Parameters.Last()); - - if (attributeIsEmbedded) - { - Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); - } - else - { - Assert.Empty(m.GlobalNamespace.GetMembers("System")); - } - }, - expectedOutput: ExpectedOutput(@" -1 -1 -0 -0 -")).VerifyDiagnostics(); - } +"""; + CreateCompilationWithIL(src, il).VerifyEmitDiagnostics(); } [Fact] - public void DelegateNaturalType_05() + public void WRN_ParamsArrayInLambdaOnly_03() { - var src1 = @" + var src = """ using System.Collections.Generic; -public class Params -{ - static public void Test1(params IEnumerable a) {} -} -"; - var src2 = @" +public delegate void D1(params IEnumerable args); +public delegate void D2(params int[] args); + class Program { static void Main() { - var x1 = Params.Test1; - - x1(1); - x1(); + D1 l1 = (params IEnumerable x) => {}; + D2 l2 = (params int[] x) => {}; + l1 = (IEnumerable x) => {}; + l2 = (int[] x) => {}; + l1(null); + l2(null); } } -"; - - var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); - var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - - comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp2.VerifyEmitDiagnostics(); +"""; + CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] - public void DelegateNaturalType_06() + public void ParamCollectionInLocalFunctionOnly() { - var src1 = @" -public class Params -{ - static public void Test1(params long[] a) {} -} -"; - var src2 = @" + var src = """ +using System.Collections.Generic; + class Program { static void Main() { - var x1 = Params.Test1; - - x1(1); - x1(); + void local (params IEnumerable x) {}; + local(1); } } -"; +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); - var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); - var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); + CompileAndVerify( + comp, + symbolValidator: (m) => + { + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); + AssertEx.Equal("void Program.
g__local|0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); - comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - comp2.VerifyDiagnostics( - // (6,18): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' - // var x1 = Params.Test1; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "Params.Test1").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(6, 18) - ); - } + Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + }).VerifyDiagnostics(); - [Fact] - public void BetterNess_01_ElementType() - { - var src = @" + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); + AssertEx.Equal("params System.Collections.Generic.IEnumerable x", parameter.ToTestDisplayString()); + VerifyParams(parameter, isParamCollection: true); + + var src2 = """ class Program { static void Main() { - int x = 1; - Test(x); - - byte y = 1; - Test(y); + void local (params long[] x) {}; + local(1); } +} +"""; - static void Test(params System.Span a) - { - System.Console.WriteLine(""long""); - } + comp = CreateCompilation(src2, options: TestOptions.ReleaseExe); - static void Test(params System.Span a) - { - System.Console.WriteLine(""int""); - } -} -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); + + comp.VerifyDiagnostics( + // (5,21): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // void local (params long[] x) {}; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params long[] x").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(5, 21) + ); + + comp = CreateCompilation(src2, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + + tree = comp.SyntaxTrees.Single(); + model = comp.GetSemanticModel(tree); + + parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single()); + AssertEx.Equal("params System.Int64[] x", parameter.ToTestDisplayString()); + VerifyParams(parameter, isParamArray: true); CompileAndVerify( comp, + symbolValidator: (m) => + { + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); + AssertEx.Equal("void Program.
g__local|0_0(System.Int64[] x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); + }).VerifyDiagnostics(); + } + + [Theory] + [InlineData(@"$""Literal{1}""")] + [InlineData(@"$""Literal"" + $""{1}""")] + public void ConversionInParamsArguments_InterpolatedStringHandler(string expression) + { + var code = @" +using System; +using System.Linq; + +M(" + expression + ", " + expression + @"); + +void M(params System.ReadOnlySpan handlers) +{ + Console.WriteLine(string.Join(Environment.NewLine, handlers.ToArray().Select(h => h.ToString()))); +} +"; + + var verifier = CompileAndVerify(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false, includeOneTimeHelpers: false) }, targetFramework: TargetFramework.Net80, verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } + Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsReadOnlySpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x11 }" } : Verification.Skipped, expectedOutput: ExpectedOutput(@" -int -int")).VerifyDiagnostics(); +literal:Literal +value:1 +alignment:0 +format: + +literal:Literal +value:1 +alignment:0 +format: +")); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", @" +{ + // Code size 122 (0x7a) + .maxstack 5 + .locals init (<>y__InlineArray2 V_0, + CustomHandler V_1) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""<>y__InlineArray2"" + IL_0008: ldloca.s V_0 + IL_000a: ldc.i4.0 + IL_000b: call ""ref CustomHandler .InlineArrayElementRef<<>y__InlineArray2, CustomHandler>(ref <>y__InlineArray2, int)"" + IL_0010: ldloca.s V_1 + IL_0012: ldc.i4.7 + IL_0013: ldc.i4.1 + IL_0014: call ""CustomHandler..ctor(int, int)"" + IL_0019: ldloca.s V_1 + IL_001b: ldstr ""Literal"" + IL_0020: call ""void CustomHandler.AppendLiteral(string)"" + IL_0025: ldloca.s V_1 + IL_0027: ldc.i4.1 + IL_0028: box ""int"" + IL_002d: ldc.i4.0 + IL_002e: ldnull + IL_002f: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0034: ldloc.1 + IL_0035: stobj ""CustomHandler"" + IL_003a: ldloca.s V_0 + IL_003c: ldc.i4.1 + IL_003d: call ""ref CustomHandler .InlineArrayElementRef<<>y__InlineArray2, CustomHandler>(ref <>y__InlineArray2, int)"" + IL_0042: ldloca.s V_1 + IL_0044: ldc.i4.7 + IL_0045: ldc.i4.1 + IL_0046: call ""CustomHandler..ctor(int, int)"" + IL_004b: ldloca.s V_1 + IL_004d: ldstr ""Literal"" + IL_0052: call ""void CustomHandler.AppendLiteral(string)"" + IL_0057: ldloca.s V_1 + IL_0059: ldc.i4.1 + IL_005a: box ""int"" + IL_005f: ldc.i4.0 + IL_0060: ldnull + IL_0061: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0066: ldloc.1 + IL_0067: stobj ""CustomHandler"" + IL_006c: ldloca.s V_0 + IL_006e: ldc.i4.2 + IL_006f: call ""System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray2, CustomHandler>(in <>y__InlineArray2, int)"" + IL_0074: call ""void Program.<
$>g__M|0_0(System.ReadOnlySpan)"" + IL_0079: ret +} +"); } [Fact] - public void BetterNess_02_ElementType() + public void OrderOfEvaluation_01_NamedArguments() { - var src = @" + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + public MyCollection() + { + System.Console.WriteLine("Create"); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(int l) + { + System.Console.WriteLine("Add"); + } +} + class Program { static void Main() { - Test(new C2()); + Test(b: GetB(), c: GetC(), a: GetA()); + } - Test(new C3()); + static void Test(int a, int b, params MyCollection c) + { } - static void Test(params System.Span a) + static int GetA() { - System.Console.WriteLine(""C1""); + System.Console.WriteLine("GetA"); + return 0; } - static void Test(params System.Span a) + static int GetB() { - System.Console.WriteLine(""C2""); + System.Console.WriteLine("GetB"); + return 0; } -} -class C1 {} -class C2 : C1 {} -class C3 : C2 {} -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + static int GetC() + { + System.Console.WriteLine("GetC"); + return 0; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - CompileAndVerify( + var verifier = CompileAndVerify( comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } - : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -C2 -C2")).VerifyDiagnostics(); + expectedOutput: @" +GetB +Create +GetC +Add +GetA +").VerifyDiagnostics(); + + // Note, the collection is created after the lexically previous argument is evaluated, + // but before the lexically following argument is evaluated. This differs from params + // array case, which is created right before the target methos is invoked, after all + // arguments are evaluated in their lexical order, which can be observed in a unit-test + // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.CodeGenTests.NamedParamsOptimizationAndParams002​ + verifier.VerifyIL("Program.Main", @" +{ + // Code size 36 (0x24) + .maxstack 3 + .locals init (int V_0, + MyCollection V_1) + IL_0000: call ""int Program.GetB()"" + IL_0005: stloc.0 + IL_0006: newobj ""MyCollection..ctor()"" + IL_000b: dup + IL_000c: call ""int Program.GetC()"" + IL_0011: callvirt ""void MyCollection.Add(int)"" + IL_0016: stloc.1 + IL_0017: call ""int Program.GetA()"" + IL_001c: ldloc.0 + IL_001d: ldloc.1 + IL_001e: call ""void Program.Test(int, int, params MyCollection)"" + IL_0023: ret +} +"); } - [Fact(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules] - public void BetterNess_03_ElementType() + [Fact] + public void OrderOfEvaluation_02_CompoundAssignment() { - var src = @" + var src = """ using System.Collections; using System.Collections.Generic; -class C1 : IEnumerable +class MyCollection : IEnumerable { - public static void M1(params C1 x) - { - } - public static void M1(params ushort[] x) + public MyCollection() { + System.Console.WriteLine("Create"); } - void Test() - { - M1('a', 'b'); - M2('a', 'b'); - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; - public static void M2(params ushort[] x) + public void Add(int l) { + System.Console.WriteLine("Add"); } - - IEnumerator IEnumerable.GetEnumerator() => throw null; - IEnumerator IEnumerable.GetEnumerator() => throw null; } -"; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics( - // (16,12): error CS1061: 'C1' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?) - // M1('a', 'b'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'a'").WithArguments("C1", "Add").WithLocation(16, 12), - // (16,17): error CS1061: 'C1' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?) - // M1('a', 'b'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'b'").WithArguments("C1", "Add").WithLocation(16, 17) - ); - } +class Program +{ + private MyCollection _c; + + static void Main() + { + System.Console.WriteLine("---Test1"); + Test1(new Program()); + System.Console.WriteLine("---Test2"); + Test2(new Program()); + System.Console.WriteLine("---Test3"); + Test3(new Program()); + } + + static void Test1(Program p) + { + p[GetA()]++; + } + + static void Test2(Program p) + { + p[GetA(), GetC()]++; + } + + static void Test3(Program p) + { + p[GetA(), GetB(), GetC()]++; + } + + int this[int a, params MyCollection c] + { + get + { + System.Console.WriteLine("Get_this {0}", c is not null); + _c = c; + return 0; + } + set + { + System.Console.WriteLine("Set_this {0}", (object)_c == c); + } + } + + + static int GetA() + { + System.Console.WriteLine("GetA"); + return 0; + } + + static int GetB() + { + System.Console.WriteLine("GetB"); + return 0; + } + + static int GetC() + { + System.Console.WriteLine("GetC"); + return 0; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + var verifier = CompileAndVerify( + comp, + expectedOutput: @" +---Test1 +GetA +Create +Get_this True +Set_this True +---Test2 +GetA +Create +GetC +Add +Get_this True +Set_this True +---Test3 +GetA +Create +GetB +Add +GetC +Add +Get_this True +Set_this True +").VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test1", @" +{ + // Code size 33 (0x21) + .maxstack 5 + .locals init (int V_0, + MyCollection V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: call ""int Program.GetA()"" + IL_0006: stloc.0 + IL_0007: newobj ""MyCollection..ctor()"" + IL_000c: stloc.1 + IL_000d: dup + IL_000e: ldloc.0 + IL_000f: ldloc.1 + IL_0010: callvirt ""int Program.this[int, params MyCollection].get"" + IL_0015: stloc.2 + IL_0016: ldloc.0 + IL_0017: ldloc.1 + IL_0018: ldloc.2 + IL_0019: ldc.i4.1 + IL_001a: add + IL_001b: callvirt ""void Program.this[int, params MyCollection].set"" + IL_0020: ret + +} +"); + + verifier.VerifyIL("Program.Test2", @" +{ + // Code size 44 (0x2c) + .maxstack 5 + .locals init (int V_0, + MyCollection V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: call ""int Program.GetA()"" + IL_0006: stloc.0 + IL_0007: newobj ""MyCollection..ctor()"" + IL_000c: dup + IL_000d: call ""int Program.GetC()"" + IL_0012: callvirt ""void MyCollection.Add(int)"" + IL_0017: stloc.1 + IL_0018: dup + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: callvirt ""int Program.this[int, params MyCollection].get"" + IL_0020: stloc.2 + IL_0021: ldloc.0 + IL_0022: ldloc.1 + IL_0023: ldloc.2 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: callvirt ""void Program.this[int, params MyCollection].set"" + IL_002b: ret +} +"); + + verifier.VerifyIL("Program.Test3", @" +{ + // Code size 55 (0x37) + .maxstack 5 + .locals init (int V_0, + MyCollection V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: call ""int Program.GetA()"" + IL_0006: stloc.0 + IL_0007: newobj ""MyCollection..ctor()"" + IL_000c: dup + IL_000d: call ""int Program.GetB()"" + IL_0012: callvirt ""void MyCollection.Add(int)"" + IL_0017: dup + IL_0018: call ""int Program.GetC()"" + IL_001d: callvirt ""void MyCollection.Add(int)"" + IL_0022: stloc.1 + IL_0023: dup + IL_0024: ldloc.0 + IL_0025: ldloc.1 + IL_0026: callvirt ""int Program.this[int, params MyCollection].get"" + IL_002b: stloc.2 + IL_002c: ldloc.0 + IL_002d: ldloc.1 + IL_002e: ldloc.2 + IL_002f: ldc.i4.1 + IL_0030: add + IL_0031: callvirt ""void Program.this[int, params MyCollection].set"" + IL_0036: ret +} +"); + } + + [Fact] + public void OrderOfEvaluation_03_ObjectInitializer() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + public MyCollection() + { + System.Console.WriteLine("Create"); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(int l) + { + System.Console.WriteLine("Add"); + } +} + +class C1 +{ + public int F1; + public int F2; +} + +class Program +{ + private MyCollection _c; + + static void Main() + { + System.Console.WriteLine("---Test1"); + Test1(); + System.Console.WriteLine("---Test2"); + Test2(); + System.Console.WriteLine("---Test3"); + Test3(); + } + + static void Test1() + { + _ = new Program() { [GetA()] = { F1 = GetF1(), F2 = GetF2() } }; + } + + static void Test2() + { + _ = new Program() { [GetA(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; + } + + static void Test3() + { + _ = new Program() { [GetA(), GetB(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; + } + + C1 this[int a, params MyCollection c] + { + get + { + System.Console.WriteLine("Get_this {0}", c is not null && (_c is null || (object)_c == c)); + _c = c; + return new C1(); + } + set + { + System.Console.WriteLine("Set_this {0}", (object)_c == c); + } + } + + + static int GetA() + { + System.Console.WriteLine("GetA"); + return 0; + } + + static int GetB() + { + System.Console.WriteLine("GetB"); + return 0; + } + + static int GetC() + { + System.Console.WriteLine("GetC"); + return 0; + } + + static int GetF1() + { + System.Console.WriteLine("GetF1"); + return 0; + } + + static int GetF2() + { + System.Console.WriteLine("GetF2"); + return 0; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + var verifier = CompileAndVerify( + comp, + expectedOutput: @" +---Test1 +GetA +Create +Get_this True +GetF1 +Get_this True +GetF2 +---Test2 +GetA +Create +GetC +Add +Get_this True +GetF1 +Get_this True +GetF2 +---Test3 +GetA +Create +GetB +Add +GetC +Add +Get_this True +GetF1 +Get_this True +GetF2 +").VerifyDiagnostics(); + + // Note, the collection is created once and that same instance is used across multiple invocation of the indexer. + // With params arrays, however, only individual elements are cached and each invocation of the indexer is getting + // a new instance of an array (with the same values inside though). This can be observed in a unit-test + // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.ObjectAndCollectionInitializerTests.DictionaryInitializerTestSideeffects001param + verifier.VerifyIL("Program.Test1", @" +{ + // Code size 53 (0x35) + .maxstack 4 + .locals init (int V_0, + MyCollection V_1) + IL_0000: newobj ""Program..ctor()"" + IL_0005: call ""int Program.GetA()"" + IL_000a: stloc.0 + IL_000b: newobj ""MyCollection..ctor()"" + IL_0010: stloc.1 + IL_0011: dup + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_0019: call ""int Program.GetF1()"" + IL_001e: stfld ""int C1.F1"" + IL_0023: ldloc.0 + IL_0024: ldloc.1 + IL_0025: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_002a: call ""int Program.GetF2()"" + IL_002f: stfld ""int C1.F2"" + IL_0034: ret +} +"); + + verifier.VerifyIL("Program.Test2", @" +{ + // Code size 64 (0x40) + .maxstack 4 + .locals init (int V_0, + MyCollection V_1) + IL_0000: newobj ""Program..ctor()"" + IL_0005: call ""int Program.GetA()"" + IL_000a: stloc.0 + IL_000b: newobj ""MyCollection..ctor()"" + IL_0010: dup + IL_0011: call ""int Program.GetC()"" + IL_0016: callvirt ""void MyCollection.Add(int)"" + IL_001b: stloc.1 + IL_001c: dup + IL_001d: ldloc.0 + IL_001e: ldloc.1 + IL_001f: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_0024: call ""int Program.GetF1()"" + IL_0029: stfld ""int C1.F1"" + IL_002e: ldloc.0 + IL_002f: ldloc.1 + IL_0030: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_0035: call ""int Program.GetF2()"" + IL_003a: stfld ""int C1.F2"" + IL_003f: ret +} +"); + + verifier.VerifyIL("Program.Test3", @" +{ + // Code size 75 (0x4b) + .maxstack 4 + .locals init (int V_0, + MyCollection V_1) + IL_0000: newobj ""Program..ctor()"" + IL_0005: call ""int Program.GetA()"" + IL_000a: stloc.0 + IL_000b: newobj ""MyCollection..ctor()"" + IL_0010: dup + IL_0011: call ""int Program.GetB()"" + IL_0016: callvirt ""void MyCollection.Add(int)"" + IL_001b: dup + IL_001c: call ""int Program.GetC()"" + IL_0021: callvirt ""void MyCollection.Add(int)"" + IL_0026: stloc.1 + IL_0027: dup + IL_0028: ldloc.0 + IL_0029: ldloc.1 + IL_002a: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_002f: call ""int Program.GetF1()"" + IL_0034: stfld ""int C1.F1"" + IL_0039: ldloc.0 + IL_003a: ldloc.1 + IL_003b: callvirt ""C1 Program.this[int, params MyCollection].get"" + IL_0040: call ""int Program.GetF2()"" + IL_0045: stfld ""int C1.F2"" + IL_004a: ret +} +"); + } + + [Fact] + public void LanguageVersion_01_Declaration() + { + var src = @" +class Program +{ + static void Test1(params System.ReadOnlySpan a) {} + static void Test2(params long[] a) {} + + void Test() + { + Test1(1); + Test2(2); + } +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (4,23): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static void Test1(params System.ReadOnlySpan a) {} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(4, 23) + ); + } + + [Fact] + public void LanguageVersion_02_CallSite() + { + var src1 = @" +public class Params +{ + static public void Test1(params System.ReadOnlySpan a) {} + static public void Test2(params long[] a) {} +} +"; + var src2 = @" +class Program +{ + void Test() + { + Params.Test1(1); + Params.Test2(2); + + Params.Test1(); + Params.Test2(); + + Params.Test1([1]); + Params.Test2([2]); + } +} +"; + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + + verify(comp1.ToMetadataReference()); + verify(comp1.EmitToImageReference()); + + void verify(MetadataReference comp1Ref) + { + var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp2.VerifyDiagnostics( + // (6,9): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Params.Test1(1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1(1)").WithArguments("params collections").WithLocation(6, 9), + // (9,9): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Params.Test1(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1()").WithArguments("params collections").WithLocation(9, 9) + ); + } + } + + [Fact] + public void LanguageVersion_03_DelegateNaturalType() + { + var src1 = @" +public class Params +{ + static public void Test1(params System.ReadOnlySpan a) {} + static public void Test2(params long[] a) {} +} +"; + var src2 = @" +class Program +{ + void Test() + { + var x1 = Params.Test1; + var x2 = Params.Test2; + + x1(1); + x2(2); + + x1(); + x2(); + + x1([1]); + x2([2]); + } +} +"; + var comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (22,30): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static public void Test1(params System.ReadOnlySpan a) {} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(22, 30) + ); + + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + + verify(comp1.ToMetadataReference()); + verify(comp1.EmitToImageReference()); + + void verify(MetadataReference comp1Ref) + { + var comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp2.VerifyDiagnostics( + // (6,18): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var x1 = Params.Test1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(6, 18) + ); + } + } + + [Fact] + public void LanguageVersion_04_DelegateNaturalType() + { + var src = @" +class Program +{ + void Test() + { + var x1 = (params System.ReadOnlySpan a) => {}; + var x2 = (params long[] a) => {}; + + x1(1); + x2(2); + + x1(); + x2(); + + x1([1]); + x2([2]); + + M1(x1); + M1(x2); + + M1((params System.ReadOnlySpan b) => {}); + M1((params long[] b) => {}); + } + + static void M1(T t) {} +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (6,19): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var x1 = (params System.ReadOnlySpan a) => {}; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan a").WithArguments("params collections").WithLocation(6, 19), + // (21,13): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1((params System.ReadOnlySpan b) => {}); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.ReadOnlySpan b").WithArguments("params collections").WithLocation(21, 13) + ); + } + + [Fact] + public void LanguageVersion_05_DelegateNaturalType() + { + var src1 = @" +public class Params +{ + static public void Test1(params System.Collections.Generic.IEnumerable a) {} + static public void Test2(params long[] a) {} +} +"; + var src2 = @" +class Program +{ + void Test1() + { + var a = Params.Test1; + M1(a); // See DelegateNaturalType_03 unit-test for an observable effect that 'params' modifier has for this invocation. + M1(Params.Test1); + } + + static void M1(T t) {} + + void Test2() + { + var b = Params.Test2; + M1(b); + M1(Params.Test2); + } +} +"; + var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (23,30): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static public void Test1(params System.Collections.Generic.IEnumerable a) {} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.Collections.Generic.IEnumerable a").WithArguments("params collections").WithLocation(23, 30) + ); + + var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); + + verify(comp1.ToMetadataReference()); + verify(comp1.EmitToImageReference()); + + void verify(MetadataReference comp1Ref) + { + var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp2.VerifyDiagnostics( + // (6,17): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var a = Params.Test1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(6, 17), + // (8,12): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // M1(Params.Test1); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Params.Test1").WithArguments("params collections").WithLocation(8, 12) + ); + } + } + + [Fact] + public void LanguageVersion_06_LambdaForDelegateWithParams() + { + var src1 = @" +public class Params +{ + static public void Test1(D1 d) {} + static public void Test2(D2 d) {} +} + +public delegate void D1(params System.Collections.Generic.IEnumerable a); +public delegate void D2(params long[] a); +"; + var src2 = @" +class Program1 +{ + void Test1() + { + Params.Test1(e1 => { }); + } +} +class Program2 +{ + void Test2() + { + Params.Test2(e2 => { }); + } +} +"; + var comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + + comp = CreateCompilation(src2 + src1, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (23,25): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public delegate void D1(params System.Collections.Generic.IEnumerable a); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params System.Collections.Generic.IEnumerable a").WithArguments("params collections").WithLocation(23, 25) + ); + + var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); + + verify(comp1.ToMetadataReference()); + verify(comp1.EmitToImageReference()); + + void verify(MetadataReference comp1Ref) + { + var comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularPreview); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll, parseOptions: TestOptions.RegularNext); + comp2.VerifyDiagnostics(); + + comp2 = CreateCompilation(src2, references: [comp1Ref], options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), parseOptions: TestOptions.Regular12); + + var tree = comp2.SyntaxTrees.Single(); + var model = comp2.GetSemanticModel(tree); + + var parameter = (IParameterSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().First()); + AssertEx.Equal("System.Collections.Generic.IEnumerable e1", parameter.ToTestDisplayString()); + Assert.False(parameter.IsParams); + Assert.False(parameter.IsParamCollection); + + CompileAndVerify(comp2, + symbolValidator: (m) => + { + var lambda = m.GlobalNamespace.GetMember("Program1.<>c.b__0_0"); + ParameterSymbol parameter = lambda.Parameters.Single(); + + VerifyParamsAndAttribute(parameter); + Assert.Equal("System.Collections.Generic.IEnumerable e1", parameter.ToTestDisplayString()); + } + ).VerifyDiagnostics(); // No language version diagnostics as expected. The 'params' modifier doesn't even make it to symbol and metadata. + } + } + + [Fact] + public void DelegateNaturalType_01() + { + var src1 = @" +public class Params +{ + static public void Test1(params System.ReadOnlySpan a) { System.Console.WriteLine(a.Length); } + static public void Test2(params long[] a) { System.Console.WriteLine(a.Length); } +} +"; + var src2 = @" +class Program +{ + static void Main() + { + var x1 = Params.Test1; + var x2 = Params.Test2; + + x1(1); + x2(2); + + x1(); + x2(); + } +} +"; + var comp = CreateCompilation(src1 + src2, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + verify(comp, attributeIsEmbedded: true); + + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + verify(comp2, attributeIsEmbedded: true); + + var comp3 = CreateCompilation(src1 + ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + var comp4 = CreateCompilation(src2, references: [comp3.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + verify(comp4, attributeIsEmbedded: false); + + var comp5 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseModule); + comp5.VerifyDiagnostics(); + + void verify(CSharpCompilation comp, bool attributeIsEmbedded) + { + // We want to test attribute embedding + Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + symbolValidator: (m) => + { + MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); + + MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); + + if (attributeIsEmbedded) + { + Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); + } + else + { + Assert.Empty(m.GlobalNamespace.GetMembers("System")); + } + }, + expectedOutput: ExpectedOutput(@" +1 +1 +0 +0 +")).VerifyDiagnostics(); + } + } + + [Fact] + public void DelegateNaturalType_02() + { + var src = @" +class Program +{ + static void Main() + { + var x1 = (params System.ReadOnlySpan a) => System.Console.WriteLine(a.Length); + var x2 = (params long[] a) => System.Console.WriteLine(a.Length); + + x1(1); + x2(2); + + x1(); + x2(); + } +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp, attributeIsEmbedded: true); + + var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp2, attributeIsEmbedded: false); + + void verify(CSharpCompilation comp, bool attributeIsEmbedded) + { + // We want to test attribute embedding + Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + symbolValidator: (m) => + { + MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); + + MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); + + // Note, no attributes on lambdas + + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); + AssertEx.Equal("void Program.<>c.
b__0_0(System.ReadOnlySpan a)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); + + MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_1"); + AssertEx.Equal("void Program.<>c.
b__0_1(System.Int64[] a)", l2.ToTestDisplayString()); + VerifyParamsAndAttribute(l2.Parameters.Last()); + + if (attributeIsEmbedded) + { + Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); + } + else + { + Assert.Empty(m.GlobalNamespace.GetMembers("System")); + } + }, + expectedOutput: ExpectedOutput(@" +1 +1 +0 +0 +")).VerifyDiagnostics(); + } + } + + [Fact] + public void DelegateNaturalType_03() + { + var src = @" +class Program +{ + static public void Test1(System.Collections.Generic.IEnumerable a) { System.Console.WriteLine("" {0}"", a is not null); } + static public void Test2(params System.Collections.Generic.IEnumerable a) { System.Console.WriteLine("" {0}"", a is not null); } + static public void Test3(params System.Collections.Generic.List a) { System.Console.WriteLine("" {0}"", a is not null); } + static public void Test4(params long[] a) { System.Console.WriteLine("" {0}"", a is not null); } + + static void Main() + { + DoTest1(); + DoTest21(); + DoTest22(); + DoTest3(); + DoTest4(); + } + + static void DoTest1() + { + var a1 = Test1; + M(a1); + } + + static void DoTest21() + { + var a2 = Test2; + M(a2)(); + } + + static void DoTest22() + { + var a2 = Test2; + M(a2)(); + } + + static void DoTest3() + { + var a3 = Test3; + M(a3)(); + } + + static void DoTest4() + { + var a4 = Test4; + M(a4)(); + } + + static T M(T t) { System.Console.WriteLine(typeof(T)); return t; } + static void M(System.Action> t) => System.Console.WriteLine(""Action""); + static void M(System.Action> t) => System.Console.WriteLine(""Action""); +} +"; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + var verifier = CompileAndVerify( + comp, + symbolValidator: (m) => + { + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod.ToTestDisplayString()); + AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params System.Collections.Generic.List arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1").DelegateInvokeMethod.ToTestDisplayString()); + AssertEx.Equal("void <>f__AnonymousDelegate2.Invoke(params T1[] arg)", m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate2`1").DelegateInvokeMethod.ToTestDisplayString()); + }, + expectedOutput: ExpectedOutput(@" +Action +<>f__AnonymousDelegate0 + True +<>f__AnonymousDelegate0 + True +<>f__AnonymousDelegate1 + True +<>f__AnonymousDelegate2`1[System.Int64] + True +")).VerifyDiagnostics(); + } + + [Fact] + public void DelegateNaturalType_04() + { + var src = @" +class Program +{ + static void Main() + { + var x1 = Test1; + var x2 = Test2; + + x1(1); + x2(2); + + x1(); + x2(); + + static void Test1(params System.ReadOnlySpan a) { System.Console.WriteLine(a.Length); } + static void Test2(params long[] a) { System.Console.WriteLine(a.Length); } + } +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp, attributeIsEmbedded: true); + + var comp1 = CreateCompilation(ParamCollectionAttributeSource, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + var comp2 = CreateCompilation(src, references: [comp1.ToMetadataReference()], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp2, attributeIsEmbedded: false); + + void verify(CSharpCompilation comp, bool attributeIsEmbedded) + { + // We want to test attribute embedding + Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + symbolValidator: (m) => + { + MethodSymbol delegateInvokeMethod1 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate0").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.ReadOnlySpan arg)", delegateInvokeMethod1.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod1.Parameters.Last(), isParamCollection: true); + + MethodSymbol delegateInvokeMethod2 = m.ContainingAssembly.GetTypeByMetadataName("<>f__AnonymousDelegate1`1").DelegateInvokeMethod; + AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); + VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); + + // Note, no attributes on local functions + + MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__Test1|0_0"); + AssertEx.Equal("void Program.
g__Test1|0_0(System.ReadOnlySpan a)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last()); + + MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.
g__Test2|0_1"); + AssertEx.Equal("void Program.
g__Test2|0_1(System.Int64[] a)", l2.ToTestDisplayString()); + VerifyParamsAndAttribute(l2.Parameters.Last()); + + if (attributeIsEmbedded) + { + Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); + } + else + { + Assert.Empty(m.GlobalNamespace.GetMembers("System")); + } + }, + expectedOutput: ExpectedOutput(@" +1 +1 +0 +0 +")).VerifyDiagnostics(); + } + } + + [Fact] + public void DelegateNaturalType_05() + { + var src1 = @" +using System.Collections.Generic; + +public class Params +{ + static public void Test1(params IEnumerable a) {} +} +"; + var src2 = @" +class Program +{ + static void Main() + { + var x1 = Params.Test1; + + x1(1); + x1(); + } +} +"; + + var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); + var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); + + comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp2.VerifyEmitDiagnostics(); + } + + [Fact] + public void DelegateNaturalType_06() + { + var src1 = @" +public class Params +{ + static public void Test1(params long[] a) {} +} +"; + var src2 = @" +class Program +{ + static void Main() + { + var x1 = Params.Test1; + + x1(1); + x1(); + } +} +"; + + var comp1 = CreateCompilation(src1, options: TestOptions.ReleaseDll); + var comp2 = CreateCompilation(src2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); + + comp2.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp2.VerifyDiagnostics( + // (6,18): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // var x1 = Params.Test1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "Params.Test1").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(6, 18) + ); + } + + [Fact] + public void BetterNess_01_ElementType() + { + var src = @" +class Program +{ + static void Main() + { + int x = 1; + Test(x); + + byte y = 1; + Test(y); + } + + static void Test(params System.Span a) + { + System.Console.WriteLine(""long""); + } + + static void Test(params System.Span a) + { + System.Console.WriteLine(""int""); + } +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? + Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } + : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +int +int")).VerifyDiagnostics(); + } + + [Fact] + public void BetterNess_02_ElementType() + { + var src = @" +class Program +{ + static void Main() + { + Test(new C2()); + + Test(new C3()); + } + + static void Test(params System.Span a) + { + System.Console.WriteLine(""C1""); + } + + static void Test(params System.Span a) + { + System.Console.WriteLine(""C2""); + } +} + +class C1 {} +class C2 : C1 {} +class C3 : C2 {} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? + Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } + : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +C2 +C2")).VerifyDiagnostics(); + } + + [Fact] + public void BetterNess_03_ElementType() + { + var src = @" +using System.Collections; +using System.Collections.Generic; + +class C1 : IEnumerable +{ + public static void M1(params C1 x) + { + } + public static void M1(params ushort[] x) + { + } + + void Test() + { + M1('a', 'b'); + M2('a', 'b'); + } + + public static void M2(params ushort[] x) + { + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); + + comp.VerifyDiagnostics( + // (7,27): error CS1061: 'C1' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?) + // public static void M1(params C1 x) + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "params C1 x").WithArguments("C1", "Add").WithLocation(7, 27) + ); + } + + [Theory] + [InlineData("System.Span", "T[]", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] + [InlineData("System.ReadOnlySpan", "T[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] + [InlineData("System.Span", "System.Collections.Generic.HashSet", null)] // rule requires array or array interface + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.Span", "System.ReadOnlySpan", "System.Span")] // cannot convert from object to int + + [InlineData("RefStructCollection", "T[]", null, new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructCollection", "RefStructCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructCollection", "GenericClassCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // rule requires span + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructCollection", "GenericClassCollection", "GenericClassCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructCollection", "NonGenericClassCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span + + [InlineData("GenericClassCollection", "T[]", null, new[] { CollectionExpressionTests.example_GenericClassCollection })] // rule requires span + [InlineData("NonGenericClassCollection", "object[]", null, new[] { CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span + [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "long[]", "System.ReadOnlySpan")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) + [InlineData("System.ReadOnlySpan", "short[]", "System.ReadOnlySpan")] // cannot convert int to short + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case + [InlineData("System.ReadOnlySpan", "T[]", "System.Int32[]")] // cannot convert long to int + // Ambiguous for inline collection expression, but 'long' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "long[]", "System.Int64[]")] // cannot convert object to long + + [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "string[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + + // PROTOTYPE(ParamsCollections): Inline collection expression picks "System.ReadOnlySpan", but that params candidate is worse because it is generic + [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] + + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + + [InlineData("System.Span", "System.Span", "System.Span")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.Span", "System.Span", "System.Span")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) + [InlineData("System.Span", "System.Span", "System.Span")] + + [InlineData("System.Span", "System.Span", "System.Span")] + [InlineData("T[]", "int[]", "System.Int32[]")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("T[]", "object[]", "System.Int32[]")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("T[]", "int?[]", "System.Int32[]")] + + [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) + [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] + + [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.IReadOnlyCollection", null)] + [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionB", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + + // PROTOTYPE(ParamsCollections): Inline collection expression picks "MyCollectionB", but that params candidate is worse because it is generic + [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case + [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case + [InlineData("MyCollectionB", "MyCollectionB", "MyCollectionB", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + + [InlineData("RefStructConvertibleFromArray", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] + [InlineData("RefStructConvertibleFromArray", "int[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructConvertibleFromArray", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("RefStructConvertibleFromArray", "object[]", "RefStructConvertibleFromArray", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] + public void BetterConversionFromExpression_01A(string type1, string type2, string expectedType, string[] additionalSources = null) // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = $$""" + using System; + class Program + { + {{generateMethod("F1", type1)}} + {{generateMethod("F1", type2)}} + {{generateMethod("F2", type2)}} + {{generateMethod("F2", type1)}} + static void Main() + { + var x = F1(1, 2, 3); + Console.WriteLine(x.GetTypeName()); + var y = F2(4, 5); + Console.WriteLine(y.GetTypeName()); + } + } + """; + var comp = CreateCompilation( + getSources(source, additionalSources), + targetFramework: TargetFramework.Net80, + options: TestOptions.ReleaseExe); + if (expectedType is { }) + { + CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" + {expectedType} + {expectedType} + """)); + } + else + { + comp.VerifyEmitDiagnostics( + // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(ReadOnlySpan)' and 'Program.F1(ReadOnlySpan)' + // var x = F1(1, 2, 3); + Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), + // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(ReadOnlySpan)' and 'Program.F2(ReadOnlySpan)' + // var y = F2(4, 5); + Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); + } + + static string getTypeParameters(string type) => + type.Contains("T[]") || type.Contains("") ? "" : ""; + + static string generateMethod(string methodName, string parameterType) => + $"static Type {methodName}{getTypeParameters(parameterType)}(params {parameterType} value) => typeof({parameterType});"; + + static string generateMethodSignature(string methodName, string parameterType) => + $"Program.{methodName}{getTypeParameters(parameterType)}(params {parameterType})"; + + static string[] getSources(string source, string[] additionalSources) + { + var builder = ArrayBuilder.GetInstance(); + builder.Add(source); + builder.Add(CollectionExpressionTests.s_collectionExtensions); + if (additionalSources is { }) builder.AddRange(additionalSources); + return builder.ToArrayAndFree(); + } + } [Theory] - [InlineData("System.Span", "T[]", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] - [InlineData("System.ReadOnlySpan", "T[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] - [InlineData("System.Span", "System.Collections.Generic.HashSet", null)] // rule requires array or array interface + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Span", null)] // cannot convert int? to int + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] + [InlineData("System.Span", "System.Span", null)] + [InlineData("System.Span", "System.Span", null)] + [InlineData("System.Span", "System.Span", null)] + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] + [InlineData("System.Span", "int?[]", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] + [InlineData("System.Span", "int[]", null)] // cannot convert int? to int + [InlineData("System.Span", "System.Collections.Generic.IEnumerable", null)] // cannot convert int? to int + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", null)] // cannot convert int? to int + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", null)] // cannot convert int? to int + [InlineData("System.Span", "System.Collections.Generic.ICollection", null)] // cannot convert int? to int + [InlineData("System.Span", "System.Collections.Generic.IList", null)] // cannot convert int? to int + [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "int[]", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", null)] // cannot convert object to int + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", null)] // cannot convert object to int + [InlineData("System.Collections.Generic.List", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.List")] + [InlineData("int[]", "object[]", null)] // rule requires span + [InlineData("int[]", "System.Collections.Generic.IReadOnlyList", null)] // rule requires span + public void BetterConversionFromExpression_01B_Empty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = $$""" + using System; + class Program + { + {{generateMethod("F1", type1)}} + {{generateMethod("F1", type2)}} + {{generateMethod("F2", type2)}} + {{generateMethod("F2", type1)}} + static void Main() + { + var a = F1(); + Console.WriteLine(a.GetTypeName()); + var b = F2(); + Console.WriteLine(b.GetTypeName()); + } + } + """; + var comp = CreateCompilation( + new[] { source, CollectionExpressionTests.s_collectionExtensions }, + targetFramework: TargetFramework.Net80, + options: TestOptions.ReleaseExe); + if (expectedType is { }) + { + CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" + {expectedType} + {expectedType} + """)); + } + else + { + comp.VerifyEmitDiagnostics( + // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' + // var a = F1(); + Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), + // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' + // var b = F2(); + Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); + } + + static string generateMethod(string methodName, string parameterType) => + $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; + + static string generateMethodSignature(string methodName, string parameterType) => + $"Program.{methodName}(params {parameterType})"; + } + + [Theory] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + // Ambiguous for inline collection expression, but 'int?' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan>")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.Span", "System.Span", "System.Span")] + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Span", "System.Span")] + // Ambiguous for inline collection expression, but 'int?' is a better conversion target than 'object' in params case + [InlineData("System.Span", "System.Span", "System.Span>")] + // Ambiguous for inline collection expression, but 'long' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + + [InlineData("System.Span", "int?[]", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] + [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "int[]", "System.Int32[]")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.IEnumerable")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Collections.Generic.IReadOnlyCollection")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Collections.Generic.IReadOnlyList")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] // cannot convert int? to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case + [InlineData("System.Span", "System.Collections.Generic.IList", "System.Collections.Generic.IList")] // cannot convert int? to int + + [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "int[]", "System.Int32[]")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.IEnumerable")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.Collections.Generic.IReadOnlyCollection")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.Collections.Generic.IReadOnlyList")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] // cannot convert object to int + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.Collections.Generic.IList")] // cannot convert object to int + + [InlineData("System.Collections.Generic.List", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.List")] + + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("int[]", "object[]", "System.Int32[]")] // rule requires span + // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case + [InlineData("int[]", "System.Collections.Generic.IReadOnlyList", "System.Int32[]")] // rule requires span + public void BetterConversionFromExpression_01B_NotEmpty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = $$""" + using System; + class Program + { + {{generateMethod("F1", type1)}} + {{generateMethod("F1", type2)}} + {{generateMethod("F2", type2)}} + {{generateMethod("F2", type1)}} + static void Main() + { + var c = F1(1, 2, 3); + Console.WriteLine(c.GetTypeName()); + var d = F2(4, 5); + Console.WriteLine(d.GetTypeName()); + } + } + """; + var comp = CreateCompilation( + new[] { source, CollectionExpressionTests.s_collectionExtensions }, + targetFramework: TargetFramework.Net80, + options: TestOptions.ReleaseExe); + if (expectedType is { }) + { + CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" + {expectedType} + {expectedType} + """)); + } + else + { + comp.VerifyEmitDiagnostics( + // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' + // var c = F1(1, 2, 3); + Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), + // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' + // var d = F2(4, 5); + Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); + } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.Span", "System.ReadOnlySpan", "System.Span")] // cannot convert from object to int + static string generateMethod(string methodName, string parameterType) => + $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; - [InlineData("RefStructCollection", "T[]", null, new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span + static string generateMethodSignature(string methodName, string parameterType) => + $"Program.{methodName}(params {parameterType})"; + } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructCollection", "RefStructCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection })] // rule requires span - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructCollection", "GenericClassCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // rule requires span - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructCollection", "GenericClassCollection", "GenericClassCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_GenericClassCollection })] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructCollection", "NonGenericClassCollection", "RefStructCollection", new[] { CollectionExpressionTests.example_RefStructCollection, CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span + [Fact] + public void BetterConversionFromExpression_02() // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string sourceA = """ + using System; + using static System.Console; - [InlineData("GenericClassCollection", "T[]", null, new[] { CollectionExpressionTests.example_GenericClassCollection })] // rule requires span - [InlineData("NonGenericClassCollection", "object[]", null, new[] { CollectionExpressionTests.example_NonGenericClassCollection })] // rule requires span - [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "long[]", "System.ReadOnlySpan")] + partial class Program + { + static void Generic(params Span value) { WriteLine("Span"); } + static void Generic(params T[] value) { WriteLine("T[]"); } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) - [InlineData("System.ReadOnlySpan", "short[]", "System.ReadOnlySpan")] // cannot convert int to short - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case - [InlineData("System.ReadOnlySpan", "T[]", "System.Int32[]")] // cannot convert long to int - // Ambiguous for inline collection expression, but 'long' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "long[]", "System.Int64[]")] // cannot convert object to long + static void Identical(params Span value) { WriteLine("Span"); } + static void Identical(params string[] value) { WriteLine("string[]"); } - [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "string[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + static void SpanDerived(params Span value) { WriteLine("Span"); } + static void SpanDerived(params object[] value) { WriteLine("object[]"); } - // PROTOTYPE(ParamsCollections): Inline collection expression picks "System.ReadOnlySpan", but that params candidate is worse because it is generic - [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] + static void ArrayDerived(params Span value) { WriteLine("Span"); } + static void ArrayDerived(params string[] value) { WriteLine("string[]"); } + } + """; - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + string sourceB1 = """ + partial class Program + { + static void Main() + { + Generic(new[] { string.Empty }); // string[] + Identical(new[] { string.Empty }); // string[] + ArrayDerived(new[] { string.Empty }); // string[] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] + Generic(string.Empty); // Span + Identical(string.Empty); // Span + SpanDerived(string.Empty); // Span - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + // Ambiguous for inline collection expression, but 'string' is a better conversion target than 'object' in params case + ArrayDerived(string.Empty); + } + } + """; + var comp = CreateCompilation( + new[] { sourceA, sourceB1 }, + targetFramework: TargetFramework.Net80, + options: TestOptions.ReleaseExe); + CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" + T[] + string[] + string[] + Span + Span + Span + string[] + """)); + } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] + [WorkItem("https://github.com/dotnet/roslyn/issues/69634")] + [Fact] + public void BetterConversionFromExpression_03() // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string sourceA = """ + using System; + using static System.Console; - [InlineData("System.Span", "System.Span", "System.Span")] + partial class Program + { + static void Unrelated(params Span value) { WriteLine("Span"); } + static void Unrelated(params string[] value) { WriteLine("string[]"); } + } + """; - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.Span", "System.Span", "System.Span")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) - [InlineData("System.Span", "System.Span", "System.Span")] + string sourceB1 = """ + partial class Program + { + static void Main() + { + Unrelated(new[] { 1 }); // Span + Unrelated(new[] { string.Empty }); // string[] - [InlineData("System.Span", "System.Span", "System.Span")] - [InlineData("T[]", "int[]", "System.Int32[]")] + Unrelated(2); // Span + Unrelated(string.Empty); // string[] + } + } + """; + var comp = CreateCompilation( + new[] { sourceA, sourceB1 }, + targetFramework: TargetFramework.Net80, + options: TestOptions.ReleaseExe); + CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" + Span + string[] + Span + string[] + """)); - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("T[]", "object[]", "System.Int32[]")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("T[]", "int?[]", "System.Int32[]")] + string sourceB2 = """ + partial class Program + { + static void Main() + { + Unrelated(new[] { default }); // error + Unrelated(default); // ambiguous + } + } + """; + comp = CreateCompilation( + new[] { sourceA, sourceB2 }, + targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( + // 1.cs(5,19): error CS0826: No best type found for implicitly-typed array + // Unrelated(new[] { default }); // error + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { default }").WithLocation(5, 19), + // 1.cs(5,19): error CS1503: Argument 1: cannot convert from '?[]' to 'int' + // Unrelated(new[] { default }); // error + Diagnostic(ErrorCode.ERR_BadArgType, "new[] { default }").WithArguments("1", "?[]", "int").WithLocation(5, 19), + // 1.cs(6,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Unrelated(params Span)' and 'Program.Unrelated(params string[])' + // Unrelated(default); // ambiguous + Diagnostic(ErrorCode.ERR_AmbigCall, "Unrelated").WithArguments("Program.Unrelated(params System.Span)", "Program.Unrelated(params string[])").WithLocation(6, 9) + ); + } - [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] + [Fact] + public void BetterConversionFromExpression_04() // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = """ + using System; + class Program + { + static void F1(int[] x, params int[] y) { throw null; } + static void F1(Span x, params ReadOnlySpan y) { x.Report(); y.Report(); } + static void F2(object x, params string[] y) { throw null; } + static void F2(string x, params Span y) { y.Report(); } + static void Main() + { + F1([1], 2); + F2("3", "4"); + } + } + """; + CreateCompilation( + new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, + targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // PROTOTYPE(ParamsCollections): Inline collection expression works in this case. + // For 'params' case it fails because: + // - For the first argument, 'int[]' and 'Span' -> neither is better + // - For the second argument, 'int' and 'int' -> neither is better vs. 'int[]' and 'ReadOnlySpan' -> ReadOnlySpan for a collection expression + // Parameters type sequences are different, tie-breaking rules do not apply. - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'short' in params case (exact target) - [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] + // 0.cs(10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[], params int[])' and 'Program.F1(Span, params ReadOnlySpan)' + // F1([1], 2); + Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(int[], params int[])", "Program.F1(System.Span, params System.ReadOnlySpan)").WithLocation(10, 9), - [InlineData("System.Collections.Generic.ICollection", "System.Collections.Generic.IReadOnlyCollection", null)] - [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionB", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + // PROTOTYPE(ParamsCollections): Inline collection expression works in this case. + // For 'params' case it fails because: + // - For the first argument, 'object' and 'string' -> string + // - For the second argument, 'string' and 'object' -> string (different direction) vs. 'string[]' and 'Span' -> neither is better for a collection expression + // Parameters type sequences are different, tie-breaking rules do not apply. - // PROTOTYPE(ParamsCollections): Inline collection expression picks "MyCollectionB", but that params candidate is worse because it is generic - [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + // 0.cs(11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object, params string[])' and 'Program.F2(string, params Span)' + // F2("3", "4"); + Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(object, params string[])", "Program.F2(string, params System.Span)").WithLocation(11, 9) + ); + } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case - [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("MyCollectionA", "MyCollectionB", "MyCollectionA", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'long' in params case - [InlineData("MyCollectionB", "MyCollectionB", "MyCollectionB", new[] { CollectionExpressionTests.example_GenericClassesWithConversion })] + [Fact] + public void BetterConversionFromExpression_05() // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = """ + using System; + class Program + { + static void F1(Span x, params int[] y) { x.Report(); y.Report(); } + static void F1(int[] x, params ReadOnlySpan y) { throw null; } + static void F2(string x, params string[] y) { y.Report(); } + static void F2(object x, params Span y) { throw null; } + static void Main() + { + F1([1], 2); + F2("3", "4"); + } + } + """; - [InlineData("RefStructConvertibleFromArray", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] - [InlineData("RefStructConvertibleFromArray", "int[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] + // Both calls are ambiguous for inline collection expressions, due to better-ness in different directions among arguments. + // For params case, there is no difference in the target type for the second argument + CompileAndVerify( + new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, + targetFramework: TargetFramework.Net80, + verify: Verification.Skipped, + expectedOutput: ExpectedOutput("[1], [2], [4], ")); + } - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructConvertibleFromArray", "T[]", "System.Int32[]", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("RefStructConvertibleFromArray", "object[]", "RefStructConvertibleFromArray", new[] { CollectionExpressionTests.example_RefStructConvertibleFromArray })] - public void BetterConversionFromExpression_01A(string type1, string type2, string expectedType, string[] additionalSources = null) // This is a clone of a unit-test from CollectionExpressionTests.cs + // Two ref struct collection types, with an implicit conversion from one to the other. + [Fact] + public void BetterConversionFromExpression_06() // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = $$""" + string source = """ using System; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create1))] + ref struct MyCollection1 + { + private readonly List _list; + public MyCollection1(List list) { _list = list; } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public static implicit operator MyCollection2(MyCollection1 c) => new(c._list); + } + [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create2))] + ref struct MyCollection2 + { + private readonly List _list; + public MyCollection2(List list) { _list = list; } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + } + static class MyCollectionBuilder + { + public static MyCollection1 Create1(scoped ReadOnlySpan items) + { + return new MyCollection1(new List(items.ToArray())); + } + public static MyCollection2 Create2(scoped ReadOnlySpan items) + { + return new MyCollection2(new List(items.ToArray())); + } + } class Program { - {{generateMethod("F1", type1)}} - {{generateMethod("F1", type2)}} - {{generateMethod("F2", type2)}} - {{generateMethod("F2", type1)}} + static void F1(params MyCollection1 c) { Console.WriteLine("MyCollection1"); } + static void F1(params MyCollection2 c) { Console.WriteLine("MyCollection2"); } + static void F2(params MyCollection2 c) { Console.WriteLine("MyCollection2"); } + static void F2(params MyCollection1 c) { Console.WriteLine("MyCollection1"); } static void Main() { - var x = F1(1, 2, 3); - Console.WriteLine(x.GetTypeName()); - var y = F2(4, 5); - Console.WriteLine(y.GetTypeName()); + F1(1, 2, 3); + F2(4, null); + F1((MyCollection1)[6]); + F1((MyCollection2)[7]); + F2((MyCollection2)[8]); } } """; - var comp = CreateCompilation( - getSources(source, additionalSources), + CompileAndVerify( + source, targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); - if (expectedType is { }) - { - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" - {expectedType} - {expectedType} + verify: Verification.Skipped, + expectedOutput: ExpectedOutput(""" + MyCollection1 + MyCollection1 + MyCollection1 + MyCollection2 + MyCollection2 """)); - } - else - { - comp.VerifyEmitDiagnostics( - // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(ReadOnlySpan)' and 'Program.F1(ReadOnlySpan)' - // var x = F1(1, 2, 3); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), - // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(ReadOnlySpan)' and 'Program.F2(ReadOnlySpan)' - // var y = F2(4, 5); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); - } - - static string getTypeParameters(string type) => - type.Contains("T[]") || type.Contains("") ? "" : ""; - - static string generateMethod(string methodName, string parameterType) => - $"static Type {methodName}{getTypeParameters(parameterType)}(params {parameterType} value) => typeof({parameterType});"; - - static string generateMethodSignature(string methodName, string parameterType) => - $"Program.{methodName}{getTypeParameters(parameterType)}(params {parameterType})"; - - static string[] getSources(string source, string[] additionalSources) - { - var builder = ArrayBuilder.GetInstance(); - builder.Add(source); - builder.Add(CollectionExpressionTests.s_collectionExtensions); - if (additionalSources is { }) builder.AddRange(additionalSources); - return builder.ToArrayAndFree(); - } } - [Theory] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Span", null)] // cannot convert int? to int - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] - [InlineData("System.Span", "System.Span", null)] - [InlineData("System.Span", "System.Span", null)] - [InlineData("System.Span", "System.Span", null)] - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", null)] - [InlineData("System.Span", "int?[]", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] - [InlineData("System.Span", "int[]", null)] // cannot convert int? to int - [InlineData("System.Span", "System.Collections.Generic.IEnumerable", null)] // cannot convert int? to int - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", null)] // cannot convert int? to int - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", null)] // cannot convert int? to int - [InlineData("System.Span", "System.Collections.Generic.ICollection", null)] // cannot convert int? to int - [InlineData("System.Span", "System.Collections.Generic.IList", null)] // cannot convert int? to int - [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "int[]", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", null)] // cannot convert object to int - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", null)] // cannot convert object to int - [InlineData("System.Collections.Generic.List", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.List")] - [InlineData("int[]", "object[]", null)] // rule requires span - [InlineData("int[]", "System.Collections.Generic.IReadOnlyList", null)] // rule requires span - public void BetterConversionFromExpression_01B_Empty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void BetterConversionFromExpression_07() // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = $$""" + string source = """ using System; class Program { - {{generateMethod("F1", type1)}} - {{generateMethod("F1", type2)}} - {{generateMethod("F2", type2)}} - {{generateMethod("F2", type1)}} + static void F1(params ReadOnlySpan value) { Console.WriteLine("int"); } + static void F1(params ReadOnlySpan value) { } + static void F2(params Span value) { Console.WriteLine("string"); } + static void F2(params Span value) { } static void Main() { - var a = F1(); - Console.WriteLine(a.GetTypeName()); - var b = F2(); - Console.WriteLine(b.GetTypeName()); + F1(1, 2, 3); + F2("a", "b"); } } """; var comp = CreateCompilation( - new[] { source, CollectionExpressionTests.s_collectionExtensions }, + source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - if (expectedType is { }) - { - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" - {expectedType} - {expectedType} - """)); - } - else - { - comp.VerifyEmitDiagnostics( - // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' - // var a = F1(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), - // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' - // var b = F2(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); - } - - static string generateMethod(string methodName, string parameterType) => - $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; - - static string generateMethodSignature(string methodName, string parameterType) => - $"Program.{methodName}(params {parameterType})"; - } - - [Theory] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Span", "System.ReadOnlySpan")] - - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.ReadOnlySpan", "System.Span", "System.Span")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] - // Ambiguous for inline collection expression, but 'int?' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan>")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.Span", "System.Span", "System.Span")] - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Span", "System.Span")] - // Ambiguous for inline collection expression, but 'int?' is a better conversion target than 'object' in params case - [InlineData("System.Span", "System.Span", "System.Span>")] - // Ambiguous for inline collection expression, but 'long' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan", "System.ReadOnlySpan")] - - [InlineData("System.Span", "int?[]", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Span")] - [InlineData("System.Span", "System.Collections.Generic.IList", "System.Span")] - - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "int[]", "System.Int32[]")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.IEnumerable")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyCollection", "System.Collections.Generic.IReadOnlyCollection")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Collections.Generic.IReadOnlyList", "System.Collections.Generic.IReadOnlyList")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] // cannot convert int? to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'int?' in params case - [InlineData("System.Span", "System.Collections.Generic.IList", "System.Collections.Generic.IList")] // cannot convert int? to int - - [InlineData("System.ReadOnlySpan", "object[]", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.ReadOnlySpan")] - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.ReadOnlySpan")] - - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "int[]", "System.Int32[]")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.IEnumerable")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyCollection", "System.Collections.Generic.IReadOnlyCollection")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IReadOnlyList", "System.Collections.Generic.IReadOnlyList")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.ICollection", "System.Collections.Generic.ICollection")] // cannot convert object to int - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("System.ReadOnlySpan", "System.Collections.Generic.IList", "System.Collections.Generic.IList")] // cannot convert object to int - [InlineData("System.Collections.Generic.List", "System.Collections.Generic.IEnumerable", "System.Collections.Generic.List")] - - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("int[]", "object[]", "System.Int32[]")] // rule requires span - // Ambiguous for inline collection expression, but 'int' is a better conversion target than 'object' in params case - [InlineData("int[]", "System.Collections.Generic.IReadOnlyList", "System.Int32[]")] // rule requires span - public void BetterConversionFromExpression_01B_NotEmpty(string type1, string type2, string expectedType) // This is a clone of a unit-test from CollectionExpressionTests.cs + // Ambiguity in case of inline collection expression + CompileAndVerify( + comp, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? + Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } + : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +int +string +")).VerifyDiagnostics(); + } + + [Fact] + public void BetterConversionFromExpression_08A() // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = $$""" - using System; + string source = """ class Program { - {{generateMethod("F1", type1)}} - {{generateMethod("F1", type2)}} - {{generateMethod("F2", type2)}} - {{generateMethod("F2", type1)}} + static void F1(params int[] value) { System.Console.WriteLine("int"); } + static void F1(params object[] value) { } static void Main() { - var c = F1(1, 2, 3); - Console.WriteLine(c.GetTypeName()); - var d = F2(4, 5); - Console.WriteLine(d.GetTypeName()); + F1(1, 2, 3); } } """; - var comp = CreateCompilation( - new[] { source, CollectionExpressionTests.s_collectionExtensions }, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); - if (expectedType is { }) - { - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($""" - {expectedType} - {expectedType} - """)); - } - else - { - comp.VerifyEmitDiagnostics( - // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' - // var c = F1(1, 2, 3); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), - // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' - // var d = F2(4, 5); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); - } - - static string generateMethod(string methodName, string parameterType) => - $"static Type {methodName}(params {parameterType} value) => typeof({parameterType});"; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - static string generateMethodSignature(string methodName, string parameterType) => - $"Program.{methodName}(params {parameterType})"; + // No behavior change (param arrays). Ambiguity in case of inline collection expression + CompileAndVerify( + comp, + expectedOutput: @"int").VerifyDiagnostics(); } [Fact] - public void BetterConversionFromExpression_02() // This is a clone of a unit-test from CollectionExpressionTests.cs + public void BetterConversionFromExpression_08B() // This is a clone of a unit-test from CollectionExpressionTests.cs { - string sourceA = """ + string source = """ using System; - using static System.Console; - - partial class Program + class Program { - static void Generic(params Span value) { WriteLine("Span"); } - static void Generic(params T[] value) { WriteLine("T[]"); } - - static void Identical(params Span value) { WriteLine("Span"); } - static void Identical(params string[] value) { WriteLine("string[]"); } - - static void SpanDerived(params Span value) { WriteLine("Span"); } - static void SpanDerived(params object[] value) { WriteLine("object[]"); } - - static void ArrayDerived(params Span value) { WriteLine("Span"); } - static void ArrayDerived(params string[] value) { WriteLine("string[]"); } + static void F2(params string[] value) { Console.WriteLine("string[]"); } + static void F2(params object[] value) { Console.WriteLine("object[]"); } + static void Main() + { + F2("a", "b"); + } } """; + CompileAndVerify(source, expectedOutput: "string[]"); + } - string sourceB1 = """ - partial class Program + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_01(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = $$""" + using System; + using static System.Console; + + class Program { + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } + static void Main() { - Generic(new[] { string.Empty }); // string[] - Identical(new[] { string.Empty }); // string[] - ArrayDerived(new[] { string.Empty }); // string[] - - Generic(string.Empty); // Span - Identical(string.Empty); // Span - SpanDerived(string.Empty); // Span - - // Ambiguous for inline collection expression, but 'string' is a better conversion target than 'object' in params case - ArrayDerived(string.Empty); + F1(); + F2(); + F1('a', 'b', 'c'); + F2('1', '2', '3'); } } """; var comp = CreateCompilation( - new[] { sourceA, sourceB1 }, + source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" - T[] - string[] - string[] - Span - Span - Span - string[] - """)); + comp.VerifyEmitDiagnostics( + // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), + // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) + ); } - [WorkItem("https://github.com/dotnet/roslyn/issues/69634")] - [Fact] - public void BetterConversionFromExpression_03() // This is a clone of a unit-test from CollectionExpressionTests.cs + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_02_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { - string sourceA = """ + string source = $$""" using System; using static System.Console; - partial class Program + class Program { - static void Unrelated(params Span value) { WriteLine("Span"); } - static void Unrelated(params string[] value) { WriteLine("string[]"); } - } - """; + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } - string sourceB1 = """ - partial class Program - { static void Main() { - Unrelated(new[] { 1 }); // Span - Unrelated(new[] { string.Empty }); // string[] - - Unrelated(2); // Span - Unrelated(string.Empty); // string[] + F1(); + F2(); } } """; var comp = CreateCompilation( - new[] { sourceA, sourceB1 }, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput(""" - Span - string[] - Span - string[] - """)); + source, + targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( + // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), + // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) + ); - string sourceB2 = """ - partial class Program + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var f1 = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "F1").Single(); + Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f1).Symbol.GetParameters().Single().Type.SpecialType); + var f2 = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "F2").Single(); + Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f2).Symbol.GetParameters().Single().Type.SpecialType); + } + + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_02_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + { + string source = $$""" + using System; + using static System.Console; + + class Program { + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } + static void Main() { - Unrelated(new[] { default }); // error - Unrelated(default); // ambiguous + F1('a', 'b', 'c'); + F2('1', '2', '3'); } } """; - comp = CreateCompilation( - new[] { sourceA, sourceB2 }, + var comp = CreateCompilation( + source, targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( - // 1.cs(5,19): error CS0826: No best type found for implicitly-typed array - // Unrelated(new[] { default }); // error - Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { default }").WithLocation(5, 19), - // 1.cs(5,19): error CS1503: Argument 1: cannot convert from '?[]' to 'int' - // Unrelated(new[] { default }); // error - Diagnostic(ErrorCode.ERR_BadArgType, "new[] { default }").WithArguments("1", "?[]", "int").WithLocation(5, 19), - // 1.cs(6,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Unrelated(params Span)' and 'Program.Unrelated(params string[])' - // Unrelated(default); // ambiguous - Diagnostic(ErrorCode.ERR_AmbigCall, "Unrelated").WithArguments("Program.Unrelated(params System.Span)", "Program.Unrelated(params string[])").WithLocation(6, 9) + // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), + // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); } - [Fact] - public void BetterConversionFromExpression_04() // This is a clone of a unit-test from CollectionExpressionTests.cs + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_03(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = """ + string source = $$""" using System; + using static System.Console; + class Program { - static void F1(int[] x, params int[] y) { throw null; } - static void F1(Span x, params ReadOnlySpan y) { x.Report(); y.Report(); } - static void F2(object x, params string[] y) { throw null; } - static void F2(string x, params Span y) { y.Report(); } + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } + static void Main() { - F1([1], 2); - F2("3", "4"); + F1(); + F2(); } } """; - CreateCompilation( - new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, - targetFramework: TargetFramework.Net80).VerifyDiagnostics( - // PROTOTYPE(ParamsCollections): Inline collection expression works in this case. - // For 'params' case it fails because: - // - For the first argument, 'int[]' and 'Span' -> neither is better - // - For the second argument, 'int' and 'int' -> neither is better vs. 'int[]' and 'ReadOnlySpan' -> ReadOnlySpan for a collection expression - // Parameters type sequences are different, tie-breaking rules do not apply. - - // 0.cs(10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[], params int[])' and 'Program.F1(Span, params ReadOnlySpan)' - // F1([1], 2); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(int[], params int[])", "Program.F1(System.Span, params System.ReadOnlySpan)").WithLocation(10, 9), - - // PROTOTYPE(ParamsCollections): Inline collection expression works in this case. - // For 'params' case it fails because: - // - For the first argument, 'object' and 'string' -> string - // - For the second argument, 'string' and 'object' -> string (different direction) vs. 'string[]' and 'Span' -> neither is better for a collection expression - // Parameters type sequences are different, tie-breaking rules do not apply. - - // 0.cs(11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object, params string[])' and 'Program.F2(string, params Span)' - // F2("3", "4"); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(object, params string[])", "Program.F2(string, params System.Span)").WithLocation(11, 9) + var comp = CreateCompilation( + source, + targetFramework: TargetFramework.Net80); + comp.VerifyEmitDiagnostics( + // (7,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(7, 20), + // (8,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 20) ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var f1 = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "F1").Single(); + Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f1).Symbol.GetParameters().Single().Type.SpecialType); + var f2 = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "F2").Single(); + Assert.NotEqual(SpecialType.System_String, model.GetSymbolInfo(f2).Symbol.GetParameters().Single().Type.SpecialType); } - [Fact] - public void BetterConversionFromExpression_05() // This is a clone of a unit-test from CollectionExpressionTests.cs + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_04_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = """ + string source = $$""" using System; + using static System.Console; + + class MyChar + { + private readonly int _i; + public MyChar(int i) { _i = i; } + public static implicit operator MyChar(int i) => new MyChar(i); + public static implicit operator char(MyChar c) => (char)c._i; + } + class Program { - static void F1(Span x, params int[] y) { x.Report(); y.Report(); } - static void F1(int[] x, params ReadOnlySpan y) { throw null; } - static void F2(string x, params string[] y) { y.Report(); } - static void F2(object x, params Span y) { throw null; } + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } + static void Main() { - F1([1], 2); - F2("3", "4"); + F1(); + F2(); } } """; - - // Both calls are ambiguous for inline collection expressions, due to better-ness in different directions among arguments. - // For params case, there is no difference in the target type for the second argument - CompileAndVerify( - new[] { source, CollectionExpressionTests.s_collectionExtensionsWithSpan }, + var comp = CreateCompilation( + source, targetFramework: TargetFramework.Net80, - verify: Verification.Skipped, - expectedOutput: ExpectedOutput("[1], [2], [4], ")); + options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics( + // (15,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(15, 20), + // (16,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(16, 20) + ); } - // Two ref struct collection types, with an implicit conversion from one to the other. - [Fact] - public void BetterConversionFromExpression_06() // This is a clone of a unit-test from CollectionExpressionTests.cs + [Theory] + [InlineData("System.ReadOnlySpan")] + [InlineData("System.Span")] + public void BetterConversionFromExpression_String_04_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = """ - using System; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create1))] - ref struct MyCollection1 - { - private readonly List _list; - public MyCollection1(List list) { _list = list; } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - public static implicit operator MyCollection2(MyCollection1 c) => new(c._list); - } - [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create2))] - ref struct MyCollection2 - { - private readonly List _list; - public MyCollection2(List list) { _list = list; } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - } - static class MyCollectionBuilder + string source = $$""" + using static System.Console; + + class MyChar { - public static MyCollection1 Create1(scoped ReadOnlySpan items) - { - return new MyCollection1(new List(items.ToArray())); - } - public static MyCollection2 Create2(scoped ReadOnlySpan items) - { - return new MyCollection2(new List(items.ToArray())); - } + private readonly int _i; + public MyChar(int i) { _i = i; } + public static implicit operator MyChar(int i) => new MyChar(i); + public static implicit operator char(MyChar c) => (char)c._i; } + class Program { - static void F1(params MyCollection1 c) { Console.WriteLine("MyCollection1"); } - static void F1(params MyCollection2 c) { Console.WriteLine("MyCollection2"); } - static void F2(params MyCollection2 c) { Console.WriteLine("MyCollection2"); } - static void F2(params MyCollection1 c) { Console.WriteLine("MyCollection1"); } + static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } + static void F1(params string value) { WriteLine("F1(string)"); } + static void F2(params string value) { WriteLine("F2(string)"); } + static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } + static void Main() { - F1(1, 2, 3); - F2(4, null); - F1((MyCollection1)[6]); - F1((MyCollection2)[7]); - F2((MyCollection2)[8]); + F1('a', 'b', 'c'); + F2('1', '2', '3'); } } """; - CompileAndVerify( + var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, - verify: Verification.Skipped, - expectedOutput: ExpectedOutput(""" - MyCollection1 - MyCollection1 - MyCollection1 - MyCollection2 - MyCollection2 - """)); + options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (14,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F1(params string value) { WriteLine("F1(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(14, 20), + // (15,20): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F2(params string value) { WriteLine("F2(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(15, 20) + ); } [Fact] - public void BetterConversionFromExpression_07() // This is a clone of a unit-test from CollectionExpressionTests.cs + public void BetterConversionFromExpression_String_05() // This is a clone of a unit-test from CollectionExpressionTests.cs { - string source = """ + string source = $$""" using System; + using System.Collections.Generic; + using static System.Console; + class Program { - static void F1(params ReadOnlySpan value) { Console.WriteLine("int"); } - static void F1(params ReadOnlySpan value) { } - static void F2(params Span value) { Console.WriteLine("string"); } - static void F2(params Span value) { } + static void F(params IEnumerable value) { WriteLine("F(IEnumerable)"); } + static void F(params string value) { WriteLine("F(string)"); } + static void Main() { - F1(1, 2, 3); - F2("a", "b"); + F(); + F('a'); } } """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,19): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // static void F(params string value) { WriteLine("F(string)"); } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "params string value").WithArguments("string", "0").WithLocation(8, 19) + ); + } + + [Fact] + public void BetterOverload_01_BetterParameterPassing() + { + // the better parameter-passing choice (https://github.com/dotnet/csharpstandard/blob/draft-v9/standard/expressions.md#12644-better-parameter-passing-mode) + // should come before collection better-ness, but after argument conversion better-ness. + // Expected output below matches legacy behavior of param arrays. + + var src = """ +class Program +{ + static void Main() + { + Test(1); + Test(1, new C2()); + } + + static void Test(in int x, params C2[] y) + { + System.Console.Write("In"); + } + + static void Test(int x, params C1[] y) + { + System.Console.Write("Val"); + } +} + +class C1 {} +class C2 : C1 {} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + CompileAndVerify(comp, expectedOutput: ExpectedOutput(@"ValIn")).VerifyDiagnostics(); + } + + [Fact] + public void BetterOverload_02_NotSameCollectionElements() + { + var src = """ +class Program +{ + static void Main() + { + Test(x: 1, y: 2); + } + + static void Test(int x, params System.ReadOnlySpan y) + { + System.Console.Write("ReadOnlySpan"); + } + + static void Test(int y, params System.Span x) + { + System.Console.Write("Span"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics( + // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(int, params ReadOnlySpan)' and 'Program.Test(int, params Span)' + // Test(x: 1, y: 2); + Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(int, params System.ReadOnlySpan)", "Program.Test(int, params System.Span)").WithLocation(5, 9) + ); + } + + [Fact] + public void BetterOverload_03_NotSameCollectionElements() + { + var src = """ +class Program +{ + static void Main() + { + Test(x: 1, y: 2); + } + + static void Test(long x, params System.ReadOnlySpan y) + { + System.Console.Write("ReadOnlySpan"); + } + + static void Test(int y, params long[] x) + { + System.Console.Write("Span"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics( + // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(long, params ReadOnlySpan)' and 'Program.Test(int, params long[])' + // Test(x: 1, y: 2); + Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(long, params System.ReadOnlySpan)", "Program.Test(int, params long[])").WithLocation(5, 9) + ); + } + + [Fact] + public void GenericInference() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + long l = 1; + int i = 2; + byte b = 3; + Test(i, b, l); + } + + static void Test(params IEnumerable b) + { + System.Console.Write(typeof(T)); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - // Ambiguity in case of inline collection expression CompileAndVerify( comp, - verify: ExecutionConditionUtil.IsMonoOrCoreClr ? - Verification.FailsILVerify with { ILVerifyMessage = "[InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc }" } - : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -int -string -")).VerifyDiagnostics(); + expectedOutput: ExpectedOutput(@"System.Int64")).VerifyDiagnostics(); } [Fact] - public void BetterConversionFromExpression_08A() // This is a clone of a unit-test from CollectionExpressionTests.cs + public void DynamicInvocation_OrdinaryMethod_01() { - string source = """ - class Program - { - static void F1(params int[] value) { System.Console.WriteLine("int"); } - static void F1(params object[] value) { } - static void Main() - { - F1(1, 2, 3); - } - } - """; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d); + Test(d, 1); + Test(d, 2, 3); + Test(2, d, 3); + Test(2, 3, d); + Test(d, [3, 4]); + + Test2(d, d); + Test2(d, 1); + Test2(d, 2, 3); + Test2(2, d, 3); + Test2(2, 3, d); + + Test2(d); + Test2(d, d); + Test2(d, 1); + Test2(d, 2, 3); + Test2(2, d, 3); + Test2(2, 3, d); + Test2(d, [3, 4]); + } + + static void Test(int a, params IEnumerable b) + { + System.Console.Write("Called"); + } + + static void Test2(int a, params T[] b) + { + System.Console.Write("Called2"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - // No behavior change (param arrays). Ambiguity in case of inline collection expression CompileAndVerify( comp, - expectedOutput: @"int").VerifyDiagnostics(); + expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] - public void BetterConversionFromExpression_08B() // This is a clone of a unit-test from CollectionExpressionTests.cs + public void DynamicInvocation_OrdinaryMethod_02_AmbiguousDynamicParamsArgument() { - string source = """ - using System; - class Program - { - static void F2(params string[] value) { Console.WriteLine("string[]"); } - static void F2(params object[] value) { Console.WriteLine("object[]"); } - static void Main() - { - F2("a", "b"); - } - } - """; - CompileAndVerify(source, expectedOutput: "string[]"); + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d); + } + + static void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (8,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + // Test(d); + Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 14) + ); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_01(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void DynamicInvocation_OrdinaryMethod_03_Warning() { - string source = $$""" - using System; - using static System.Console; + var src = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d1 = System.DateTime.Now; + Test1(d1); // Called2 + + dynamic d2 = new[] { 1 }; + Test1(d2); // Called1 + Test2(1, d1); // Called3 + Test2(1, d2); // Called5 + + int x = 1; + Test2(x, d1); // Called3 + Test2(x, d2); // Called4 - static void Main() - { - F1(); - F2(); - F1('a', 'b', 'c'); - F2('1', '2', '3'); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($$""" - F1({{spanType}}) - F2({{spanType}}) - F1({{spanType}}) - F2({{spanType}}) - """)); + dynamic d3 = (byte)1; + Test3(d3, 1, 2); // Called7 + Test3(d3, x, x); // Called6 + + dynamic d4 = x; + Test4((byte)d3, x, x); // Called8 + Test4(d3, x, x); // Called9 + Test4(d3, d4, d4); // Called9 + } + + static void Test1(params IEnumerable b) => System.Console.Write("Called1"); + static void Test1(System.DateTime b) => System.Console.Write("Called2"); + + static void Test2(int x, System.DateTime b) => System.Console.Write("Called3"); + static void Test2(long x, IEnumerable b) => System.Console.Write("Called4"); + static void Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); + + static void Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); + static void Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); + + static void Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); + static void Test4(byte x, long y, long z) => System.Console.Write("Called9"); +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called2Called1Called3Called5Called3Called4Called7Called6Called8Called9Called9"). + VerifyDiagnostics( + // (8,9): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test1(d1); // Called2 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d1)").WithArguments("Test1").WithLocation(8, 9), + // (11,9): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test1(d2); // Called1 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d2)").WithArguments("Test1").WithLocation(11, 9), + // (12,9): warning CS9503: One or more overloads of method 'Test2' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test2(1, d1); // Called3 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test2(1, d1)").WithArguments("Test2").WithLocation(12, 9), + // (13,9): warning CS9503: One or more overloads of method 'Test2' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test2(1, d2); // Called5 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test2(1, d2)").WithArguments("Test2").WithLocation(13, 9), + // (20,9): warning CS9503: One or more overloads of method 'Test3' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test3(d3, 1, 2); // Called7 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test3(d3, 1, 2)").WithArguments("Test3").WithLocation(20, 9), + // (25,9): warning CS9503: One or more overloads of method 'Test4' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test4(d3, x, x); // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test4(d3, x, x)").WithArguments("Test4").WithLocation(25, 9), + // (26,9): warning CS9503: One or more overloads of method 'Test4' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test4(d3, d4, d4); // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test4(d3, d4, d4)").WithArguments("Test4").WithLocation(26, 9) + ); } - [Theory] - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_02_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void DynamicInvocation_OrdinaryMethod_04() { - string source = $$""" - using System; - using static System.Console; + var src = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d, 2); + } - static void Main() - { - F1(); - F2(); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80); - comp.VerifyEmitDiagnostics( - // (13,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(params ReadOnlySpan)' and 'Program.F1(params string)' - // F1(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments($"Program.F1(params {spanType})", "Program.F1(params string)").WithLocation(13, 9), - // (14,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(params string)' and 'Program.F2(params ReadOnlySpan)' - // F2(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(params string)", $"Program.F2(params {spanType})").WithLocation(14, 9)); + static void Test(int a, params IEnumerable b) + { + System.Console.Write("Called {0}", b is not null); + } + + static void Test(int a, System.DateTime b) + { + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called True").VerifyDiagnostics(); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_02_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void DynamicInvocation_OrdinaryMethod_05() { - string source = $$""" - using System; - using static System.Console; + var src = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d, 2, 3); + } - static void Main() - { - F1('a', 'b', 'c'); - F2('1', '2', '3'); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80); + static void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - // Inline collection expression results in an ambiguity. - comp.VerifyEmitDiagnostics( - // (13,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F1('a', 'b', 'c')").WithArguments("string", "0").WithLocation(13, 9), - // (13,12): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'a'").WithArguments("string", "Add").WithLocation(13, 12), - // (13,17): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'b'").WithArguments("string", "Add").WithLocation(13, 17), - // (13,22): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'c'").WithArguments("string", "Add").WithLocation(13, 22), - // (14,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F2('1', '2', '3')").WithArguments("string", "0").WithLocation(14, 9), - // (14,12): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'1'").WithArguments("string", "Add").WithLocation(14, 12), - // (14,17): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'2'").WithArguments("string", "Add").WithLocation(14, 17), - // (14,22): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'3'").WithArguments("string", "Add").WithLocation(14, 22) - ); + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); } - [Theory] - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_03(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void DynamicInvocation_OrdinaryMethod_06_TypeArgumentInferenceError() { - string source = $$""" - using System; - using static System.Console; + var src1 = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d, 2, 3); + } - static void Main() - { - F1(); - F2(); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80); - comp.VerifyEmitDiagnostics( - // (13,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(params ReadOnlySpan)' and 'Program.F1(params string)' - // F1(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments($"Program.F1(params {spanType})", $"Program.F1(params string)").WithLocation(13, 9), - // (14,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(params string)' and 'Program.F2(params ReadOnlySpan)' - // F2(); - Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments($"Program.F2(params string)", $"Program.F2(params {spanType})").WithLocation(14, 9)); - } + static void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_04_Empty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs - { - string source = $$""" - using System; - using static System.Console; + comp1.VerifyDiagnostics( + // (8,9): error CS9501: The type arguments for method 'Program.Test(params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + // Test(d, 2, 3); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(d, 2, 3)").WithArguments("Program.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) + ); - class MyChar - { - private readonly int _i; - public MyChar(int i) { _i = i; } - public static implicit operator MyChar(int i) => new MyChar(i); - public static implicit operator char(MyChar c) => (char)c._i; - } + var src2 = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(d, 2, 3); + } - static void Main() - { - F1(); - F2(); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($$""" - F1({{spanType}}) - F2({{spanType}}) - """)); + static void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp2, + expectedOutput: @"Called").VerifyDiagnostics(); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules - [InlineData("System.ReadOnlySpan")] - [InlineData("System.Span")] - public void BetterConversionFromExpression_String_04_NotEmpty(string spanType) // This is a clone of a unit-test from CollectionExpressionTests.cs + [Fact] + public void DynamicInvocation_OrdinaryMethod_07_TypeArgumentInferenceError() { - string source = $$""" - using static System.Console; - - class MyChar - { - private readonly int _i; - public MyChar(int i) { _i = i; } - public static implicit operator MyChar(int i) => new MyChar(i); - public static implicit operator char(MyChar c) => (char)c._i; - } + var src1 = """ +using System.Collections.Generic; - class Program - { - static void F1(params {{spanType}} value) { WriteLine("F1({{spanType}})"); } - static void F1(params string value) { WriteLine("F1(string)"); } - static void F2(params string value) { WriteLine("F2(string)"); } - static void F2(params {{spanType}} value) { WriteLine("F2({{spanType}})"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(0, d, 2, 3); + } - static void Main() - { - F1('a', 'b', 'c'); - F2('1', '2', '3'); - } - } - """; - var comp = CreateCompilation( - source, - targetFramework: TargetFramework.Net80, - options: TestOptions.ReleaseExe); + static void Test(T a, params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - // PROTOTYPE(ParamsCollections): Inline collection expression picks a different overload and succeeds. - comp.VerifyDiagnostics( - // (20,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F1('a', 'b', 'c')").WithArguments("string", "0").WithLocation(20, 9), - // (20,12): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'a'").WithArguments("string", "Add").WithLocation(20, 12), - // (20,17): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'b'").WithArguments("string", "Add").WithLocation(20, 17), - // (20,22): 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?) - // F1('a', 'b', 'c'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'c'").WithArguments("string", "Add").WithLocation(20, 22), - // (21,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F2('1', '2', '3')").WithArguments("string", "0").WithLocation(21, 9), - // (21,12): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'1'").WithArguments("string", "Add").WithLocation(21, 12), - // (21,17): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'2'").WithArguments("string", "Add").WithLocation(21, 17), - // (21,22): 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?) - // F2('1', '2', '3'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'3'").WithArguments("string", "Add").WithLocation(21, 22) + comp1.VerifyDiagnostics( + // (8,9): error CS9501: The type arguments for method 'Program.Test(T, params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + // Test(0, d, 2, 3); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(0, d, 2, 3)").WithArguments("Program.Test(T, params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) ); - } - [Fact(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules - public void BetterConversionFromExpression_String_05() // This is a clone of a unit-test from CollectionExpressionTests.cs - { - string source = $$""" - using System; - using System.Collections.Generic; - using static System.Console; + var src2 = """ +using System.Collections.Generic; - class Program - { - static void F(params IEnumerable value) { WriteLine("F(IEnumerable)"); } - static void F(params string value) { WriteLine("F(string)"); } +class Program +{ + static void Main() + { + dynamic d = 1; + Test(0, d, 2, 3); + } - static void Main() - { - F(); - F('a'); - } - } - """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F(); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F()").WithArguments("string", "0").WithLocation(12, 9), - // (13,9): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // F('a'); - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "F('a')").WithArguments("string", "0").WithLocation(13, 9), - // (13,11): 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?) - // F('a'); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'a'").WithArguments("string", "Add").WithLocation(13, 11)); + static void Test(T a, params IEnumerable b) + { + System.Console.Write("Called"); + } +} +"""; + var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp2, + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void BetterOverload_01_BetterParameterPassing() + public void DynamicInvocation_OrdinaryMethod_08_HideByOverride() { - // the better parameter-passing choice (https://github.com/dotnet/csharpstandard/blob/draft-v9/standard/expressions.md#12644-better-parameter-passing-mode) - // should come before collection better-ness, but after argument conversion better-ness. - // Expected output below matches legacy behavior of param arrays. - var src = """ +using System.Collections.Generic; + class Program { static void Main() { - Test(1); - Test(1, new C2()); + dynamic d = 1; + new C2().Test(d, 2, 3); } +} - static void Test(in int x, params C2[] y) - { - System.Console.Write("In"); - } +class C1 +{ + public virtual void Test(params IEnumerable b){} +} - static void Test(int x, params C1[] y) +class C2 : C1 +{ + public override void Test(params IEnumerable b) { - System.Console.Write("Val"); + System.Console.Write("Called"); } } - -class C1 {} -class C2 : C1 {} """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: ExpectedOutput(@"ValIn")).VerifyDiagnostics(); + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void BetterOverload_02_NotSameCollectionElements() + public void DynamicInvocation_OrdinaryMethod_09_HideBySignature() { var src = """ +using System.Collections.Generic; + class Program { static void Main() { - Test(x: 1, y: 2); + dynamic d = 1; + new C2().Test(d, 2, 3); } +} - static void Test(int x, params System.ReadOnlySpan y) - { - System.Console.Write("ReadOnlySpan"); - } +class C1 +{ + public void Test(params IEnumerable b){} +} - static void Test(int y, params System.Span x) +class C2 : C1 +{ + new public void Test(params IEnumerable b) { - System.Console.Write("Span"); + System.Console.Write("Called"); } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(int, params ReadOnlySpan)' and 'Program.Test(int, params Span)' - // Test(x: 1, y: 2); - Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(int, params System.ReadOnlySpan)", "Program.Test(int, params System.Span)").WithLocation(5, 9) - ); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void BetterOverload_03_NotSameCollectionElements() + public void DynamicInvocation_OrdinaryMethod_10_HideBySignature() { var src = """ +using System.Collections.Generic; + class Program { static void Main() { - Test(x: 1, y: 2); + dynamic d = 1; + new C2().Test(d, 2, 3); } +} - static void Test(long x, params System.ReadOnlySpan y) - { - System.Console.Write("ReadOnlySpan"); - } +class C0 +{ + public virtual void Test(params IEnumerable b){} +} - static void Test(int y, params long[] x) +class C1 : C0 +{ + public override void Test(params IEnumerable b){} +} + +class C2 : C1 +{ + new public void Test(params IEnumerable b) { - System.Console.Write("Span"); + System.Console.Write("Called"); } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(long, params ReadOnlySpan)' and 'Program.Test(int, params long[])' - // Test(x: 1, y: 2); - Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(long, params System.ReadOnlySpan)", "Program.Test(int, params long[])").WithLocation(5, 9) - ); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void GenericInference() + public void DynamicInvocation_OrdinaryMethod_11_HideBySignature() { var src = """ using System.Collections.Generic; @@ -4012,66 +5713,106 @@ class Program { static void Main() { - long l = 1; - int i = 2; - byte b = 3; - Test(i, b, l); + dynamic d = 1; + new C3().Test(d, 2, 3); } +} - static void Test(params IEnumerable b) +class C1 +{ + public void Test(params IEnumerable b){} +} + +class C2 : C1 +{ + new public virtual void Test(params IEnumerable b) {} +} + +class C3 : C2 +{ + public override void Test(params IEnumerable b) { - System.Console.Write(typeof(T)); + System.Console.Write("Called"); } } """; - var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, - expectedOutput: ExpectedOutput(@"System.Int64")).VerifyDiagnostics(); + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_01() + public void DynamicInvocation_OrdinaryMethod_12_HideByName() { - var src = """ + var src1 = """ using System.Collections.Generic; +public class C1 +{ + public void Test(params IEnumerable b){} +} +"""; + + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); + + var src2 = """ +Public Class C2 + Inherits C1 + + Public Shadows Sub Test(ParamArray b As Long()) + System.Console.Write("Called") + End Sub +End Class +"""; + + MetadataReference comp1Ref = comp1.EmitToImageReference(); + var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); + + var src = """ class Program { static void Main() { dynamic d = 1; - Test(d); - Test(d, 1); - Test(d, 2, 3); - Test(2, d, 3); - Test(2, 3, d); - Test(d, [3, 4]); + new C2().Test(d, 2, 3); + } +} +"""; + var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - Test2(d, d); - Test2(d, 1); - Test2(d, 2, 3); - Test2(2, d, 3); - Test2(2, 3, d); + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); + } - Test2(d); - Test2(d, d); - Test2(d, 1); - Test2(d, 2, 3); - Test2(2, d, 3); - Test2(2, 3, d); - Test2(d, [3, 4]); + [Fact] + public void DynamicInvocation_OrdinaryMethod_13_DoNotHideByApplicability() + { + var src = """ +class Program +{ + static void Main() + { + dynamic d = 1L; + new C2().Test(d); } +} - static void Test(int a, params IEnumerable b) +class C1 +{ + public void Test(long a) { - System.Console.Write("Called"); + System.Console.Write("long"); } +} - static void Test2(int a, params T[] b) +class C2 : C1 +{ + public void Test(int b) { - System.Console.Write("Called2"); + System.Console.Write("int"); } } """; @@ -4079,11 +5820,11 @@ static void Test2(int a, params T[] b) CompileAndVerify( comp, - expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); + expectedOutput: @"long").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_02_AmbiguousDynamicParamsArgument() + public void DynamicInvocation_OrdinaryMethod_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; @@ -4092,27 +5833,35 @@ class Program { static void Main() { - dynamic d = 1; - Test(d); + dynamic d = 1L; + new C1().Test(1, d, 2); } +} - static void Test(params IEnumerable b) +class C1 +{ + public void Test(long a1, params IEnumerable a2) { - System.Console.Write("Called"); + System.Console.Write("long"); + } + + public void Test(int b1, int b2, int b3) + { + System.Console.Write("int"); } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( - // (8,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - // Test(d); - Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 14) + // (8,9): warning CS9503: One or more overloads of method 'Test' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new C1().Test(1, d, 2); + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "new C1().Test(1, d, 2)").WithArguments("Test").WithLocation(8, 9) ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_03_Warning() + public void DynamicInvocation_OrdinaryMethod_15_Warning() { var src = """ using System.Collections.Generic; @@ -4120,75 +5869,37 @@ public void DynamicInvocation_OrdinaryMethod_03_Warning() class Program { static void Main() - { - dynamic d1 = System.DateTime.Now; - Test1(d1); // Called2 - - dynamic d2 = new[] { 1 }; - Test1(d2); // Called1 - Test2(1, d1); // Called3 - Test2(1, d2); // Called5 - - int x = 1; - Test2(x, d1); // Called3 - Test2(x, d2); // Called4 - - dynamic d3 = (byte)1; - Test3(d3, 1, 2); // Called7 - Test3(d3, x, x); // Called6 - - dynamic d4 = x; - Test4((byte)d3, x, x); // Called8 - Test4(d3, x, x); // Called9 - Test4(d3, d4, d4); // Called9 + { + dynamic d1 = 1; + Test1((int)d1); + try + { + Test1(d1); + } + catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) + { + System.Console.Write("Failed"); + } } static void Test1(params IEnumerable b) => System.Console.Write("Called1"); static void Test1(System.DateTime b) => System.Console.Write("Called2"); - - static void Test2(int x, System.DateTime b) => System.Console.Write("Called3"); - static void Test2(long x, IEnumerable b) => System.Console.Write("Called4"); - static void Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); - - static void Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); - static void Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); - - static void Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); - static void Test4(byte x, long y, long z) => System.Console.Write("Called9"); } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, - expectedOutput: @"Called2Called1Called3Called5Called3Called4Called7Called6Called8Called9Called9"). + expectedOutput: @"Called1Failed"). VerifyDiagnostics( - // (8,9): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test1(d1); // Called2 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d1)").WithArguments("Test1").WithLocation(8, 9), - // (11,9): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test1(d2); // Called1 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d2)").WithArguments("Test1").WithLocation(11, 9), - // (12,9): warning CS9503: One or more overloads of method 'Test2' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test2(1, d1); // Called3 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test2(1, d1)").WithArguments("Test2").WithLocation(12, 9), - // (13,9): warning CS9503: One or more overloads of method 'Test2' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test2(1, d2); // Called5 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test2(1, d2)").WithArguments("Test2").WithLocation(13, 9), - // (20,9): warning CS9503: One or more overloads of method 'Test3' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test3(d3, 1, 2); // Called7 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test3(d3, 1, 2)").WithArguments("Test3").WithLocation(20, 9), - // (25,9): warning CS9503: One or more overloads of method 'Test4' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test4(d3, x, x); // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test4(d3, x, x)").WithArguments("Test4").WithLocation(25, 9), - // (26,9): warning CS9503: One or more overloads of method 'Test4' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test4(d3, d4, d4); // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test4(d3, d4, d4)").WithArguments("Test4").WithLocation(26, 9) + // (11,13): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // Test1(d1); + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d1)").WithArguments("Test1").WithLocation(11, 13) ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_04() + public void DynamicInvocation_LocalFunction_01() { var src = """ using System.Collections.Generic; @@ -4198,16 +5909,29 @@ class Program static void Main() { dynamic d = 1; - Test(d, 2); - } + Test(d); + Test(d, 1); + Test(d, 2, 3); + Test(2, d, 3); + Test(2, 3, d); + Test(2, [3, d]); - static void Test(int a, params IEnumerable b) - { - System.Console.Write("Called {0}", b is not null); - } + Test2(d); + Test2(d, 1); + Test2(d, 2, 3); + Test2(2, d, 3); + Test2(2, 3, d); + Test2(d, [3, 4]); - static void Test(int a, System.DateTime b) - { + void Test(int a, params IEnumerable b) + { + System.Console.Write("Called"); + } + + void Test2(int a, params int[] b) + { + System.Console.Write("Called2"); + } } } """; @@ -4215,11 +5939,11 @@ static void Test(int a, System.DateTime b) CompileAndVerify( comp, - expectedOutput: @"Called True").VerifyDiagnostics(); + expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_05() + public void DynamicInvocation_LocalFunction_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; @@ -4229,24 +5953,26 @@ class Program static void Main() { dynamic d = 1; - Test(d, 2, 3); - } + Test(d); - static void Test(params IEnumerable b) - { - System.Console.Write("Called"); + static void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (8,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + // Test(d); + Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 14) + ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_06_TypeArgumentInferenceError() + public void DynamicInvocation_LocalFunction_06_TypeArgumentInferenceError() { var src1 = """ using System.Collections.Generic; @@ -4257,20 +5983,20 @@ static void Main() { dynamic d = 1; Test(d, 2, 3); - } - static void Test(params IEnumerable b) - { - System.Console.Write("Called"); + void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } } } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( - // (8,9): error CS9501: The type arguments for method 'Program.Test(params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + // (8,9): error CS9501: The type arguments for method 'Test(params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. // Test(d, 2, 3); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(d, 2, 3)").WithArguments("Program.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(d, 2, 3)").WithArguments("Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) ); var src2 = """ @@ -4282,11 +6008,11 @@ static void Main() { dynamic d = 1; Test(d, 2, 3); - } - static void Test(params IEnumerable b) - { - System.Console.Write("Called"); + void Test(params IEnumerable b) + { + System.Console.Write("Called"); + } } } """; @@ -4298,7 +6024,7 @@ static void Test(params IEnumerable b) } [Fact] - public void DynamicInvocation_OrdinaryMethod_07_TypeArgumentInferenceError() + public void DynamicInvocation_LocalFunction_07_TypeArgumentInferenceError() { var src1 = """ using System.Collections.Generic; @@ -4309,20 +6035,20 @@ static void Main() { dynamic d = 1; Test(0, d, 2, 3); - } - static void Test(T a, params IEnumerable b) - { - System.Console.Write("Called"); + void Test(T a, params IEnumerable b) + { + System.Console.Write("Called"); + } } } """; var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp1.VerifyDiagnostics( - // (8,9): error CS9501: The type arguments for method 'Program.Test(T, params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + // (8,9): error CS9501: The type arguments for method 'Test(T, params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. // Test(0, d, 2, 3); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(0, d, 2, 3)").WithArguments("Program.Test(T, params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(0, d, 2, 3)").WithArguments("Test(T, params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) ); var src2 = """ @@ -4334,11 +6060,11 @@ static void Main() { dynamic d = 1; Test(0, d, 2, 3); - } - static void Test(T a, params IEnumerable b) - { - System.Console.Write("Called"); + void Test(T a, params IEnumerable b) + { + System.Console.Write("Called"); + } } } """; @@ -4350,7 +6076,7 @@ static void Test(T a, params IEnumerable b) } [Fact] - public void DynamicInvocation_OrdinaryMethod_08_HideByOverride() + public void DynamicInvocation_Delegate_01() { var src = """ using System.Collections.Generic; @@ -4359,68 +6085,47 @@ class Program { static void Main() { + D test = Test; dynamic d = 1; - new C2().Test(d, 2, 3); - } -} - -class C1 -{ - public virtual void Test(params IEnumerable b){} -} + test(d); + test(d, 1); + test(d, 2, 3); + test(2, d, 3); + test(2, 3, d); + test(2, [3, d]); -class C2 : C1 -{ - public override void Test(params IEnumerable b) - { - System.Console.Write("Called"); - } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + D2 test2 = Test2; + test2(d); + test2(d, d); + test2(d, 1); + test2(d, 2, 3); + test2(2, d, 3); + test2(2, 3, d); + test2(d, [3, 4]); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + void Test(int a, IEnumerable b) + { + System.Console.Write("Called"); } - - [Fact] - public void DynamicInvocation_OrdinaryMethod_09_HideBySignature() + void Test2(int a, int[] b) { - var src = """ -using System.Collections.Generic; - -class Program -{ - static void Main() - { - dynamic d = 1; - new C2().Test(d, 2, 3); + System.Console.Write("Called2"); + } } } -class C1 -{ - public void Test(params IEnumerable b){} -} - -class C2 : C1 -{ - new public void Test(params IEnumerable b) - { - System.Console.Write("Called"); - } -} +delegate void D(int a, params IEnumerable b); +delegate void D2(int a, params int[] b); """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, - expectedOutput: @"Called").VerifyDiagnostics(); + expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_10_HideBySignature() + public void DynamicInvocation_Delegate_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; @@ -4429,38 +6134,30 @@ class Program { static void Main() { + D test = Test; dynamic d = 1; - new C2().Test(d, 2, 3); - } -} - -class C0 -{ - public virtual void Test(params IEnumerable b){} -} - -class C1 : C0 -{ - public override void Test(params IEnumerable b){} -} + test(d); -class C2 : C1 -{ - new public void Test(params IEnumerable b) - { - System.Console.Write("Called"); + static void Test(IEnumerable b) + { + System.Console.Write("Called"); + } } } + +delegate void D(params IEnumerable b); """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (9,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'D.Invoke(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + // test(d); + Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("D.Invoke(params System.Collections.Generic.IEnumerable)").WithLocation(9, 14) + ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_11_HideBySignature() + public void DynamicInvocation_Indexer_01() { var src = """ using System.Collections.Generic; @@ -4469,26 +6166,47 @@ class Program { static void Main() { + var c1 = new C1(); dynamic d = 1; - new C3().Test(d, 2, 3); + _ = c1[d]; + _ = c1[d, 1]; + _ = c1[d, 2, 3]; + _ = c1[2, d, 3]; + _ = c1[2, 3, d]; + _ = c1[d, [3, 4]]; + + var c2 = new C2(); + + _ = c2[d]; + _ = c2[d, d]; + _ = c2[d, 1]; + _ = c2[d, 2, 3]; + _ = c2[2, d, 3]; + _ = c2[2, 3, d]; + _ = c2[d, [3, 4]]; } } class C1 { - public void Test(params IEnumerable b){} -} - -class C2 : C1 -{ - new public virtual void Test(params IEnumerable b) {} + public int this[int a, params IEnumerable b] + { + get + { + System.Console.Write("Called"); + return 0; + } + } } - -class C3 : C2 +class C2 { - public override void Test(params IEnumerable b) + public int this[int a, params int[] b] { - System.Console.Write("Called"); + get + { + System.Console.Write("Called2"); + return 0; + } } } """; @@ -4496,79 +6214,94 @@ public override void Test(params IEnumerable b) CompileAndVerify( comp, - expectedOutput: @"Called").VerifyDiagnostics(); + expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_12_HideByName() + public void DynamicInvocation_Indexer_02_AmbiguousDynamicParamsArgument() { - var src1 = """ + var src = """ using System.Collections.Generic; -public class C1 -{ - public void Test(params IEnumerable b){} -} -"""; - - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); - - var src2 = """ -Public Class C2 - Inherits C1 - - Public Shadows Sub Test(ParamArray b As Long()) - System.Console.Write("Called") - End Sub -End Class -"""; - - MetadataReference comp1Ref = comp1.EmitToImageReference(); - var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); - - var src = """ class Program { static void Main() { dynamic d = 1; - new C2().Test(d, 2, 3); + _ = new Program()[d]; + } + + int this[params IEnumerable b] + { + get + { + System.Console.Write("Called"); + return 0; + } } } """; - var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (8,27): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.this[params IEnumerable]', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + // _ = new Program()[d]; + Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.this[params System.Collections.Generic.IEnumerable]").WithLocation(8, 27) + ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_13_DoNotHideByApplicability() + public void DynamicInvocation_Indexer_03_Warning() { var src = """ +using System.Collections.Generic; + class Program { static void Main() { - dynamic d = 1L; - new C2().Test(d); + dynamic d1 = System.DateTime.Now; + _ = new Test1()[d1]; // Called2 + + dynamic d2 = new[] { 1 }; + _ = new Test1()[d2]; // Called1 + _ = new Test2()[1, d1]; // Called3 + _ = new Test2()[1, d2]; // Called5 + + int x = 1; + _ = new Test2()[x, d1]; // Called3 + _ = new Test2()[x, d2]; // Called4 + + dynamic d3 = (byte)1; + _ = new Test3()[d3, 1, 2]; // Called7 + _ = new Test3()[d3, x, x]; // Called6 + + dynamic d4 = x; + _ = new Test4()[(byte)d3, x, x]; // Called8 + _ = new Test4()[d3, x, x]; // Called9 + _ = new Test4()[d3, d4, d4]; // Called9 } -} -class C1 -{ - public void Test(long a) + class Test1 { - System.Console.Write("long"); + public int this[params IEnumerable b] { get { System.Console.Write("Called1"); return 0; } } + public int this[System.DateTime b] { get { System.Console.Write("Called2"); return 0; } } } -} - -class C2 : C1 -{ - public void Test(int b) + class Test2 { - System.Console.Write("int"); + public int this[int x, System.DateTime b] { get { System.Console.Write("Called3"); return 0; } } + public int this[long x, IEnumerable b] { get { System.Console.Write("Called4"); return 0; } } + public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called5"); return 0; } } + } + class Test3 + { + public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called6"); return 0; } } + public int this[byte x, byte y, byte z] { get { System.Console.Write("Called7"); return 0; } } + } + class Test4 + { + public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called8"); return 0; } } + public int this[byte x, long y, long z] { get { System.Console.Write("Called9"); return 0; } } } } """; @@ -4576,11 +6309,34 @@ public void Test(int b) CompileAndVerify( comp, - expectedOutput: @"long").VerifyDiagnostics(); + expectedOutput: @"Called2Called1Called3Called5Called3Called4Called7Called6Called8Called9Called9"). + VerifyDiagnostics( + // (8,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test1()[d1]; // Called2 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test1()[d1]").WithLocation(8, 13), + // (11,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test1()[d2]; // Called1 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test1()[d2]").WithLocation(11, 13), + // (12,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test2()[1, d1]; // Called3 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test2()[1, d1]").WithLocation(12, 13), + // (13,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test2()[1, d2]; // Called5 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test2()[1, d2]").WithLocation(13, 13), + // (20,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test3()[d3, 1, 2]; // Called7 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test3()[d3, 1, 2]").WithLocation(20, 13), + // (25,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test4()[d3, x, x]; // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test4()[d3, x, x]").WithLocation(25, 13), + // (26,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new Test4()[d3, d4, d4]; // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test4()[d3, d4, d4]").WithLocation(26, 13) + ); } [Fact] - public void DynamicInvocation_OrdinaryMethod_14_DoNotFilterBasedOnBetterFunctionMember() + public void DynamicInvocation_Indexer_04() { var src = """ using System.Collections.Generic; @@ -4589,35 +6345,31 @@ class Program { static void Main() { - dynamic d = 1L; - new C1().Test(1, d, 2); + dynamic d = 1; + _ = new Program()[d, 2]; } -} -class C1 -{ - public void Test(long a1, params IEnumerable a2) + int this[int a, params IEnumerable b] { - System.Console.Write("long"); + get + { + System.Console.Write("Called {0}", b is not null); + return 0; + } } - public void Test(int b1, int b2, int b3) - { - System.Console.Write("int"); - } + int this[int a, System.DateTime b] => 0; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (8,9): warning CS9503: One or more overloads of method 'Test' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new C1().Test(1, d, 2); - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "new C1().Test(1, d, 2)").WithArguments("Test").WithLocation(8, 9) - ); + CompileAndVerify( + comp, + expectedOutput: @"Called True").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_OrdinaryMethod_15_Warning() + public void DynamicInvocation_Indexer_05() { var src = """ using System.Collections.Generic; @@ -4626,36 +6378,29 @@ class Program { static void Main() { - dynamic d1 = 1; - Test1((int)d1); - try - { - Test1(d1); - } - catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) + dynamic d = 1; + _ = new Program()[d, 2, 3]; + } + + int this[params IEnumerable b] + { + get { - System.Console.Write("Failed"); + System.Console.Write("Called"); + return 0; } } - - static void Test1(params IEnumerable b) => System.Console.Write("Called1"); - static void Test1(System.DateTime b) => System.Console.Write("Called2"); } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, - expectedOutput: @"Called1Failed"). - VerifyDiagnostics( - // (11,13): warning CS9503: One or more overloads of method 'Test1' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // Test1(d1); - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod, "Test1(d1)").WithArguments("Test1").WithLocation(11, 13) - ); + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_LocalFunction_01() + public void DynamicInvocation_Indexer_08_HideByOverride() { var src = """ using System.Collections.Generic; @@ -4665,28 +6410,23 @@ class Program static void Main() { dynamic d = 1; - Test(d); - Test(d, 1); - Test(d, 2, 3); - Test(2, d, 3); - Test(2, 3, d); - Test(2, [3, d]); - - Test2(d); - Test2(d, 1); - Test2(d, 2, 3); - Test2(2, d, 3); - Test2(2, 3, d); - Test2(d, [3, 4]); + _ = new C2()[d, 2, 3]; + } +} - void Test(int a, params IEnumerable b) - { - System.Console.Write("Called"); - } +class C1 +{ + public virtual T this[params IEnumerable b] => default; +} - void Test2(int a, params int[] b) +class C2 : C1 +{ + public override T this[params IEnumerable b] + { + get { - System.Console.Write("Called2"); + System.Console.Write("Called"); + return default; } } } @@ -4695,11 +6435,11 @@ void Test2(int a, params int[] b) CompileAndVerify( comp, - expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2").VerifyDiagnostics(); + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_LocalFunction_02_AmbiguousDynamicParamsArgument() + public void DynamicInvocation_Indexer_09_HideBySignature() { var src = """ using System.Collections.Generic; @@ -4709,28 +6449,38 @@ class Program static void Main() { dynamic d = 1; - Test(d); + _ = new C2()[d, 2, 3]; + } +} - static void Test(params IEnumerable b) +class C1 +{ + public T this[params IEnumerable b] => default; +} + +class C2 : C1 +{ + new public T this[params IEnumerable b] + { + get { System.Console.Write("Called"); + return default; } } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (8,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - // Test(d); - Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 14) - ); + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_LocalFunction_06_TypeArgumentInferenceError() + public void DynamicInvocation_Indexer_10_HideBySignature() { - var src1 = """ + var src = """ using System.Collections.Generic; class Program @@ -4738,24 +6488,43 @@ class Program static void Main() { dynamic d = 1; - Test(d, 2, 3); + _ = new C2()[d, 2, 3]; + } +} - void Test(params IEnumerable b) +class C0 +{ + public virtual T this[params IEnumerable b] => default; +} + +class C1 : C0 +{ + public override T this[params IEnumerable b] => default; +} + +class C2 : C1 +{ + new public T this[params IEnumerable b] + { + get { System.Console.Write("Called"); + return default; } } } """; - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - comp1.VerifyDiagnostics( - // (8,9): error CS9501: The type arguments for method 'Test(params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - // Test(d, 2, 3); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(d, 2, 3)").WithArguments("Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) - ); + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); + } - var src2 = """ + [Fact] + public void DynamicInvocation_Indexer_11_HideBySignature() + { + var src = """ using System.Collections.Generic; class Program @@ -4763,125 +6532,132 @@ class Program static void Main() { dynamic d = 1; - Test(d, 2, 3); + _ = new C3()[d, 2, 3]; + } +} - void Test(params IEnumerable b) +class C1 +{ + public T this[params IEnumerable b] => default; +} + +class C2 : C1 +{ + new public virtual T this[params IEnumerable b] => default; +} + +class C3 : C2 +{ + public override T this[params IEnumerable b] + { + get { System.Console.Write("Called"); + return default; } } } """; - var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( - comp2, + comp, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_LocalFunction_07_TypeArgumentInferenceError() + public void DynamicInvocation_Indexer_12_HideByName() { var src1 = """ using System.Collections.Generic; -class Program +public class C1 { - static void Main() - { - dynamic d = 1; - Test(0, d, 2, 3); - - void Test(T a, params IEnumerable b) - { - System.Console.Write("Called"); - } - } + public int this[int x, params IEnumerable b] => default; } """; - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - comp1.VerifyDiagnostics( - // (8,9): error CS9501: The type arguments for method 'Test(T, params IEnumerable)' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - // Test(0, d, 2, 3); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections, "Test(0, d, 2, 3)").WithArguments("Test(T, params System.Collections.Generic.IEnumerable)").WithLocation(8, 9) - ); + var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); var src2 = """ -using System.Collections.Generic; +Public Class C2 + Inherits C1 + + Public Shadows Readonly Default Property Item(x as Integer, ParamArray b As Long()) As Integer + Get + System.Console.Write("Called") + Return 0 + End Get + End Property +End Class +"""; + MetadataReference comp1Ref = comp1.EmitToImageReference(); + var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); + + var src = """ class Program { static void Main() { dynamic d = 1; - Test(0, d, 2, 3); - - void Test(T a, params IEnumerable b) - { - System.Console.Write("Called"); - } + _ = new C2()[4, d, 2, 3]; } } """; - var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( - comp2, + comp, expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Delegate_01() + public void DynamicInvocation_Indexer_13_DoNotHideByApplicability() { var src = """ -using System.Collections.Generic; - class Program { static void Main() { - D test = Test; - dynamic d = 1; - test(d); - test(d, 1); - test(d, 2, 3); - test(2, d, 3); - test(2, 3, d); - test(2, [3, d]); - - D2 test2 = Test2; - test2(d); - test2(d, d); - test2(d, 1); - test2(d, 2, 3); - test2(2, d, 3); - test2(2, 3, d); - test2(d, [3, 4]); + dynamic d = 1L; + _ = new C2()[d]; + } +} - void Test(int a, IEnumerable b) +class C1 +{ + public long this[long a] + { + get { - System.Console.Write("Called"); + System.Console.Write("long"); + return a; } - void Test2(int a, int[] b) + } +} + +class C2 : C1 +{ + public int this[int b] + { + get { - System.Console.Write("Called2"); + System.Console.Write("int"); + return b; } } } - -delegate void D(int a, params IEnumerable b); -delegate void D2(int a, params int[] b); """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); CompileAndVerify( comp, - expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); + expectedOutput: @"long").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Delegate_02_AmbiguousDynamicParamsArgument() + public void DynamicInvocation_Indexer_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; @@ -4890,30 +6666,43 @@ class Program { static void Main() { - D test = Test; - dynamic d = 1; - test(d); + dynamic d = 1L; + _ = new C1()[1, d, 2]; + } +} - static void Test(IEnumerable b) +class C1 +{ + public long this[long a1, params IEnumerable a2] + { + get { - System.Console.Write("Called"); + System.Console.Write("long"); + return a1; } } -} -delegate void D(params IEnumerable b); + public int this[int b1, int b2, int b3] + { + get + { + System.Console.Write("int"); + return b1; + } + } +} """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( - // (9,14): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'D.Invoke(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - // test(d); - Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("D.Invoke(params System.Collections.Generic.IEnumerable)").WithLocation(9, 14) + // (8,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // _ = new C1()[1, d, 2]; + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new C1()[1, d, 2]").WithLocation(8, 13) ); } [Fact] - public void DynamicInvocation_Indexer_01() + public void DynamicInvocation_Constructor_01() { var src = """ using System.Collections.Generic; @@ -4922,46 +6711,36 @@ class Program { static void Main() { - var c1 = new C1(); dynamic d = 1; - _ = c1[d]; - _ = c1[d, 1]; - _ = c1[d, 2, 3]; - _ = c1[2, d, 3]; - _ = c1[2, 3, d]; - _ = c1[d, [3, 4]]; - - var c2 = new C2(); + new Test(d); + new Test(d, 1); + new Test(d, 2, 3); + new Test(2, d, 3); + new Test(2, 3, d); + new Test(d, [3, 4]); - _ = c2[d]; - _ = c2[d, d]; - _ = c2[d, 1]; - _ = c2[d, 2, 3]; - _ = c2[2, d, 3]; - _ = c2[2, 3, d]; - _ = c2[d, [3, 4]]; + new Test2(d); + new Test2(d, d); + new Test2(d, 1); + new Test2(d, 2, 3); + new Test2(2, d, 3); + new Test2(2, 3, d); + new Test2(d, [3, 4]); } -} -class C1 -{ - public int this[int a, params IEnumerable b] + class Test { - get + public Test(int a, params IEnumerable b) { System.Console.Write("Called"); - return 0; } } -} -class C2 -{ - public int this[int a, params int[] b] + + class Test2 { - get + public Test2(int a, params int[] b) { System.Console.Write("Called2"); - return 0; } } } @@ -4974,7 +6753,7 @@ class C2 } [Fact] - public void DynamicInvocation_Indexer_02_AmbiguousDynamicParamsArgument() + public void DynamicInvocation_Constructor_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; @@ -4984,15 +6763,14 @@ class Program static void Main() { dynamic d = 1; - _ = new Program()[d]; + new Test(d); } - int this[params IEnumerable b] + class Test { - get + public Test(params IEnumerable b) { System.Console.Write("Called"); - return 0; } } } @@ -5000,14 +6778,14 @@ int this[params IEnumerable b] var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( - // (8,27): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.this[params IEnumerable]', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - // _ = new Program()[d]; - Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.this[params System.Collections.Generic.IEnumerable]").WithLocation(8, 27) + // (8,18): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.Test.Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + // new Test(d); + Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.Test.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 18) ); } [Fact] - public void DynamicInvocation_Indexer_03_Warning() + public void DynamicInvocation_Constructor_03_Warning() { var src = """ using System.Collections.Generic; @@ -5017,47 +6795,50 @@ class Program static void Main() { dynamic d1 = System.DateTime.Now; - _ = new Test1()[d1]; // Called2 + new Test1(d1); // Called2 dynamic d2 = new[] { 1 }; - _ = new Test1()[d2]; // Called1 - _ = new Test2()[1, d1]; // Called3 - _ = new Test2()[1, d2]; // Called5 + new Test1(d2); // Called1 + new Test2(1, d1); // Called3 + new Test2(1, d2); // Called5 int x = 1; - _ = new Test2()[x, d1]; // Called3 - _ = new Test2()[x, d2]; // Called4 + new Test2(x, d1); // Called3 + new Test2(x, d2); // Called4 dynamic d3 = (byte)1; - _ = new Test3()[d3, 1, 2]; // Called7 - _ = new Test3()[d3, x, x]; // Called6 + new Test3(d3, 1, 2); // Called7 + new Test3(d3, x, x); // Called6 dynamic d4 = x; - _ = new Test4()[(byte)d3, x, x]; // Called8 - _ = new Test4()[d3, x, x]; // Called9 - _ = new Test4()[d3, d4, d4]; // Called9 + new Test4((byte)d3, x, x); // Called8 + new Test4(d3, x, x); // Called9 + new Test4(d3, d4, d4); // Called9 } class Test1 { - public int this[params IEnumerable b] { get { System.Console.Write("Called1"); return 0; } } - public int this[System.DateTime b] { get { System.Console.Write("Called2"); return 0; } } + public Test1(params IEnumerable b) => System.Console.Write("Called1"); + public Test1(System.DateTime b) => System.Console.Write("Called2"); } + class Test2 { - public int this[int x, System.DateTime b] { get { System.Console.Write("Called3"); return 0; } } - public int this[long x, IEnumerable b] { get { System.Console.Write("Called4"); return 0; } } - public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called5"); return 0; } } + public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); + public Test2(long x, IEnumerable b) => System.Console.Write("Called4"); + public Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); } + class Test3 { - public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called6"); return 0; } } - public int this[byte x, byte y, byte z] { get { System.Console.Write("Called7"); return 0; } } + public Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); + public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); } + class Test4 { - public int this[byte x, params IEnumerable b] { get { System.Console.Write("Called8"); return 0; } } - public int this[byte x, long y, long z] { get { System.Console.Write("Called9"); return 0; } } + public Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); + public Test4(byte x, long y, long z) => System.Console.Write("Called9"); } } """; @@ -5067,32 +6848,32 @@ class Test4 comp, expectedOutput: @"Called2Called1Called3Called5Called3Called4Called7Called6Called8Called9Called9"). VerifyDiagnostics( - // (8,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test1()[d1]; // Called2 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test1()[d1]").WithLocation(8, 13), - // (11,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test1()[d2]; // Called1 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test1()[d2]").WithLocation(11, 13), - // (12,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test2()[1, d1]; // Called3 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test2()[1, d1]").WithLocation(12, 13), - // (13,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test2()[1, d2]; // Called5 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test2()[1, d2]").WithLocation(13, 13), - // (20,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test3()[d3, 1, 2]; // Called7 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test3()[d3, 1, 2]").WithLocation(20, 13), - // (25,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test4()[d3, x, x]; // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test4()[d3, x, x]").WithLocation(25, 13), - // (26,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new Test4()[d3, d4, d4]; // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new Test4()[d3, d4, d4]").WithLocation(26, 13) + // (8,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test1(d1); // Called2 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test1(d1)").WithLocation(8, 9), + // (11,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test1(d2); // Called1 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test1(d2)").WithLocation(11, 9), + // (12,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test2(1, d1); // Called3 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test2(1, d1)").WithLocation(12, 9), + // (13,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test2(1, d2); // Called5 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test2(1, d2)").WithLocation(13, 9), + // (20,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test3(d3, 1, 2); // Called7 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test3(d3, 1, 2)").WithLocation(20, 9), + // (25,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test4(d3, x, x); // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test4(d3, x, x)").WithLocation(25, 9), + // (26,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test4(d3, d4, d4); // Called9 + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test4(d3, d4, d4)").WithLocation(26, 9) ); } [Fact] - public void DynamicInvocation_Indexer_04() + public void DynamicInvocation_Constructor_04() { var src = """ using System.Collections.Generic; @@ -5102,19 +6883,20 @@ class Program static void Main() { dynamic d = 1; - _ = new Program()[d, 2]; + new Test(d, 2); } - int this[int a, params IEnumerable b] + class Test { - get + public Test(int a, params IEnumerable b) { System.Console.Write("Called {0}", b is not null); - return 0; } - } - int this[int a, System.DateTime b] => 0; + public Test(int a, System.DateTime b) + { + } + } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); @@ -5125,7 +6907,7 @@ static void Main() } [Fact] - public void DynamicInvocation_Indexer_05() + public void DynamicInvocation_Constructor_05() { var src = """ using System.Collections.Generic; @@ -5135,15 +6917,14 @@ class Program static void Main() { dynamic d = 1; - _ = new Program()[d, 2, 3]; + new Test(d, 2, 3); } - int this[params IEnumerable b] + class Test { - get + public Test(params IEnumerable b) { System.Console.Write("Called"); - return 0; } } } @@ -5156,7 +6937,7 @@ int this[params IEnumerable b] } [Fact] - public void DynamicInvocation_Indexer_08_HideByOverride() + public void DynamicInvocation_Constructor_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; @@ -5165,37 +6946,130 @@ class Program { static void Main() { - dynamic d = 1; - _ = new C2()[d, 2, 3]; + dynamic d = 1L; + new Test(1, d, 2); } } -class C1 +class Test { - public virtual T this[params IEnumerable b] => default; + public Test(long a1, params IEnumerable a2) + { + System.Console.Write("long"); + } + + public Test(int b1, int b2, int b3) + { + System.Console.Write("int"); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (8,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + // new Test(1, d, 2); + Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test(1, d, 2)").WithLocation(8, 9) + ); + } + + [Fact] + public void DynamicInvocation_Constructor_16_Abstract() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + new Test(d); + } + + abstract class Test + { + public Test(int a, params IEnumerable b) + { + System.Console.Write("Called"); + } + } } +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (8,9): error CS0144: Cannot create an instance of the abstract type or interface 'Program.Test' + // new Test(d); + Diagnostic(ErrorCode.ERR_NoNewAbstract, "new Test(d)").WithArguments("Program.Test").WithLocation(8, 9) + ); + } + + [Fact] + public void DynamicInvocation_ConstructorInitializer_01() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + new C01(d); + new C02(d); + new C03(d); + new C04(d); + new C05(d); + new C06(d); + + new C07(d); + new C09(d); + new C10(d); + new C11(d); + new C12(d); + new C13(d); + } + + class C01(dynamic d) : Test(d); + class C02(dynamic d) : Test(d, 1); + class C03(dynamic d) : Test(d, 2, 3); + class C04(dynamic d) : Test(2, d, 3); + class C05(dynamic d) : Test(2, 3, d); + class C06(dynamic d) : Test(d, [3, 4]); + + class C07(dynamic d) : Test2(d); + class C09(dynamic d) : Test2(d, 1); + class C10(dynamic d) : Test2(d, 2, 3); + class C11(dynamic d) : Test2(2, d, 3); + class C12(dynamic d) : Test2(2, 3, d); + class C13(dynamic d) : Test2(d, [3, 4]); -class C2 : C1 -{ - public override T this[params IEnumerable b] + class Test { - get + public Test(int a, params IEnumerable b) { System.Console.Write("Called"); - return default; + } + } + + class Test2 + { + public Test2(int a, params int[] b) + { + System.Console.Write("Called2"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( comp, - expectedOutput: @"Called").VerifyDiagnostics(); + expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Indexer_09_HideBySignature() + public void DynamicInvocation_ConstructorInitializer_02_AmbiguousDynamicParamsArgument() { var src = """ using System.Collections.Generic; @@ -5205,36 +7079,31 @@ class Program static void Main() { dynamic d = 1; - _ = new C2()[d, 2, 3]; + new C(d); } -} -class C1 -{ - public T this[params IEnumerable b] => default; -} + class C(dynamic d) : Test(d); -class C2 : C1 -{ - new public T this[params IEnumerable b] + class Test { - get + public Test(params IEnumerable b) { System.Console.Write("Called"); - return default; } } } """; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (11,30): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C(dynamic d) : Test(d); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d)").WithLocation(11, 30) + ); } [Fact] - public void DynamicInvocation_Indexer_10_HideBySignature() + public void DynamicInvocation_ConstructorInitializer_03_MultipleCandidates() { var src = """ using System.Collections.Generic; @@ -5243,42 +7112,105 @@ class Program { static void Main() { - dynamic d = 1; - _ = new C2()[d, 2, 3]; + dynamic d1 = System.DateTime.Now; + new C01(d1); + + dynamic d2 = new[] { 1 }; + new C02(d2); + new C03(d1); + new C04(d2); + + int x = 1; + new C05(x, d1); + new C06(x, d2); + + dynamic d3 = (byte)1; + new C07(d3); + new C08(d3, x); + + dynamic d4 = x; + new C09(d3, x); + new C10(d3, x); + new C11(d3, d4); } -} -class C0 -{ - public virtual T this[params IEnumerable b] => default; -} + class C01(dynamic d1) : Test1(d1); + + class C02(dynamic d2) : Test1(d2); + class C03(dynamic d1) : Test2(1, d1); + class C04(dynamic d2) : Test2(1, d2); + + class C05(int x, dynamic d1) : Test2(x, d1); + class C06(int x, dynamic d2) : Test2(x, d2); -class C1 : C0 -{ - public override T this[params IEnumerable b] => default; -} + class C07(dynamic d3) : Test3(d3, 1, 2); + class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 -class C2 : C1 -{ - new public T this[params IEnumerable b] + class C09(dynamic d3, int x) : Test4((byte)d3, x, x); // Called8 + class C10(dynamic d3, int x) : Test4(d3, x, x); + class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); + + class Test1 { - get - { - System.Console.Write("Called"); - return default; - } + public Test1(params IEnumerable b) => System.Console.Write("Called1"); + public Test1(System.DateTime b) => System.Console.Write("Called2"); + } + + class Test2 + { + public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); + public Test2(long x, IEnumerable b) => System.Console.Write("Called4"); + public Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); + } + + class Test3 + { + public Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); + public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); + } + + class Test4 + { + public Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); + public Test4(byte x, long y, long z) => System.Console.Write("Called9"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (29,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C01(dynamic d1) : Test1(d1); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d1)").WithLocation(29, 34), + // (31,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C02(dynamic d2) : Test1(d2); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d2)").WithLocation(31, 34), + // (32,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C03(dynamic d1) : Test2(1, d1); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d1)").WithLocation(32, 34), + // (33,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C04(dynamic d2) : Test2(1, d2); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d2)").WithLocation(33, 34), + // (35,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C05(int x, dynamic d1) : Test2(x, d1); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d1)").WithLocation(35, 41), + // (36,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C06(int x, dynamic d2) : Test2(x, d2); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d2)").WithLocation(36, 41), + // (38,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C07(dynamic d3) : Test3(d3, 1, 2); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, 1, 2)").WithLocation(38, 34), + // (42,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C10(dynamic d3, int x) : Test4(d3, x, x); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, x, x)").WithLocation(42, 41), + // (43,46): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, d4, d4)").WithLocation(43, 46) + ); } [Fact] - public void DynamicInvocation_Indexer_11_HideBySignature() + public void DynamicInvocation_ConstructorInitializer_04() { var src = """ using System.Collections.Generic; @@ -5288,28 +7220,20 @@ class Program static void Main() { dynamic d = 1; - _ = new C3()[d, 2, 3]; + new C(d); } -} - -class C1 -{ - public T this[params IEnumerable b] => default; -} -class C2 : C1 -{ - new public virtual T this[params IEnumerable b] => default; -} + class C(dynamic d) : Test(d, 2); -class C3 : C2 -{ - public override T this[params IEnumerable b] + class Test { - get + public Test(int a, params IEnumerable b) + { + System.Console.Write("Called {0}", b is not null); + } + + public Test(int a, System.DateTime b) { - System.Console.Write("Called"); - return default; } } } @@ -5318,89 +7242,30 @@ public override T this[params IEnumerable b] CompileAndVerify( comp, - expectedOutput: @"Called").VerifyDiagnostics(); + expectedOutput: @"Called True").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Indexer_12_HideByName() + public void DynamicInvocation_ConstructorInitializer_05() { - var src1 = """ + var src = """ using System.Collections.Generic; -public class C1 -{ - public int this[int x, params IEnumerable b] => default; -} -"""; - - var comp1 = CreateCompilation(src1, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseDll); - - var src2 = """ -Public Class C2 - Inherits C1 - - Public Shadows Readonly Default Property Item(x as Integer, ParamArray b As Long()) As Integer - Get - System.Console.Write("Called") - Return 0 - End Get - End Property -End Class -"""; - - MetadataReference comp1Ref = comp1.EmitToImageReference(); - var comp2 = CreateVisualBasicCompilation(src2, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard).Concat(comp1Ref)); - - var src = """ class Program { static void Main() { dynamic d = 1; - _ = new C2()[4, d, 2, 3]; - } -} -"""; - var comp = CreateCompilation(src, references: new[] { comp1Ref, comp2.EmitToImageReference() }, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); - } - - [Fact] - public void DynamicInvocation_Indexer_13_DoNotHideByApplicability() - { - var src = """ -class Program -{ - static void Main() - { - dynamic d = 1L; - _ = new C2()[d]; - } -} - -class C1 -{ - public long this[long a] - { - get - { - System.Console.Write("long"); - return a; - } + new C(d); } -} -class C2 : C1 -{ - public int this[int b] + class C(dynamic d) : Test(d, 2, 3); + + class Test { - get + public Test(params IEnumerable b) { - System.Console.Write("int"); - return b; + System.Console.Write("Called"); } } } @@ -5409,11 +7274,11 @@ public int this[int b] CompileAndVerify( comp, - expectedOutput: @"long").VerifyDiagnostics(); + expectedOutput: @"Called").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Indexer_14_DoNotFilterBasedOnBetterFunctionMember() + public void DynamicInvocation_ConstructorInitializer_14_DoNotFilterBasedOnBetterFunctionMember() { var src = """ using System.Collections.Generic; @@ -5423,709 +7288,734 @@ class Program static void Main() { dynamic d = 1L; - _ = new C1()[1, d, 2]; + new C(d); } } -class C1 +class C(dynamic d) : Test(1, d, 2); + +class Test { - public long this[long a1, params IEnumerable a2] + public Test(long a1, params IEnumerable a2) { - get - { - System.Console.Write("long"); - return a1; - } + System.Console.Write("long"); } - public int this[int b1, int b2, int b3] + public Test(int b1, int b2, int b3) { - get - { - System.Console.Write("int"); - return b1; - } + System.Console.Write("int"); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( - // (8,13): warning CS9504: One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // _ = new C1()[1, d, 2]; - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer, "new C1()[1, d, 2]").WithLocation(8, 13) + // (12,26): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. + // class C(dynamic d) : Test(1, d, 2); + Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d, 2)").WithLocation(12, 26) ); } [Fact] - public void DynamicInvocation_Constructor_01() + public void ExpressionTree() { - var src = """ -using System.Collections.Generic; + var src = @" +using System.Linq.Expressions; class Program { static void Main() { - dynamic d = 1; - new Test(d); - new Test(d, 1); - new Test(d, 2, 3); - new Test(2, d, 3); - new Test(2, 3, d); - new Test(d, [3, 4]); - - new Test2(d); - new Test2(d, d); - new Test2(d, 1); - new Test2(d, 2, 3); - new Test2(2, d, 3); - new Test2(2, 3, d); - new Test2(d, [3, 4]); + Expression e1 = () => Test(); + Expression e2 = () => Test(1); + Expression e3 = () => Test(2, 3); + Expression e4 = () => Test([]); } - class Test + static void Test(params System.Collections.Generic.IEnumerable a) { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called"); + } +} +"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + // PROTOTYPE(ParamsCollections): report more specific error. + comp.VerifyDiagnostics( + // (8,46): error CS9175: An expression tree may not contain a collection expression. + // Expression e1 = () => Test(); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test()").WithLocation(8, 46), + // (9,46): error CS9175: An expression tree may not contain a collection expression. + // Expression e2 = () => Test(1); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test(1)").WithLocation(9, 46), + // (10,46): error CS9175: An expression tree may not contain a collection expression. + // Expression e3 = () => Test(2, 3); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test(2, 3)").WithLocation(10, 46), + // (11,51): error CS9175: An expression tree may not contain a collection expression. + // Expression e4 = () => Test([]); + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[]").WithLocation(11, 51) + ); } + + [Fact] + public void MetadataImport_01_Method() + { + // public class Params + // { + // static public void Test1(params System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } + // static public void Test2(params long[] a) { System.Console.Write("Test2"); } + // } + string il = @" +.class public auto ansi beforefieldinit Params + extends [mscorlib]System.Object +{ + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - class Test2 + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed { - public Test2(int a, params int[] b) - { - System.Console.Write("Called2"); - } + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 8 + + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2Called2").VerifyDiagnostics(); +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret + } +} +"; + + var comp = CreateCompilationWithIL("", il); + + var test1 = comp.GetMember("Params.Test1").Parameters.Last(); + var test2 = comp.GetMember("Params.Test2").Parameters.Last(); + + VerifyParamsAndAttribute(test1, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true); + + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); + + VerifyParamsAndAttribute(test1, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true); + + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + + comp = CreateCompilationWithIL("", il); + + test1 = comp.GetMember("Params.Test1").Parameters.Last(); + test2 = comp.GetMember("Params.Test2").Parameters.Last(); + + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); + + VerifyParamsAndAttribute(test1, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true); + + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + + var src = @" +class Program +{ + static void Main() + { + Params.Test1(1); + Params.Test2(2); + } +} +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } - [Fact] - public void DynamicInvocation_Constructor_02_AmbiguousDynamicParamsArgument() - { - var src = """ -using System.Collections.Generic; + [Fact] + public void MetadataImport_02_Method() + { + // public class Params + // { + // static public void Test1([ParamCollectionAttribute, ParamArrayAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } + // static public void Test2([ParamCollectionAttribute, ParamArrayAttribute] long[] a) { System.Console.Write("Test2"); } + // } + string il = @" +.class public auto ansi beforefieldinit Params + extends [mscorlib]System.Object +{ + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } + + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 8 + + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } -class Program -{ - static void Main() + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - dynamic d = 1; - new Test(d); + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } +} - class Test +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - public Test(params IEnumerable b) - { - System.Console.Write("Called"); - } + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); +"; - comp.VerifyDiagnostics( - // (8,18): error CS9502: Ambiguity between expanded and normal forms of non-array params collection parameter of 'Program.Test.Test(params IEnumerable)', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - // new Test(d); - Diagnostic(ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument, "d").WithArguments("Program.Test.Test(params System.Collections.Generic.IEnumerable)").WithLocation(8, 18) - ); - } + var comp = CreateCompilationWithIL("", il); - [Fact] - public void DynamicInvocation_Constructor_03_Warning() - { - var src = """ -using System.Collections.Generic; + var test1 = comp.GetMember("Params.Test1").Parameters.Last(); + var test2 = comp.GetMember("Params.Test2").Parameters.Last(); -class Program -{ - static void Main() - { - dynamic d1 = System.DateTime.Now; - new Test1(d1); // Called2 - - dynamic d2 = new[] { 1 }; - new Test1(d2); // Called1 - new Test2(1, d1); // Called3 - new Test2(1, d2); // Called5 - - int x = 1; - new Test2(x, d1); // Called3 - new Test2(x, d2); // Called4 + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - dynamic d3 = (byte)1; - new Test3(d3, 1, 2); // Called7 - new Test3(d3, x, x); // Called6 + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); - dynamic d4 = x; - new Test4((byte)d3, x, x); // Called8 - new Test4(d3, x, x); // Called9 - new Test4(d3, d4, d4); // Called9 - } + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - class Test1 - { - public Test1(params IEnumerable b) => System.Console.Write("Called1"); - public Test1(System.DateTime b) => System.Console.Write("Called2"); - } + var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; + AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - class Test2 - { - public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); - public Test2(long x, IEnumerable b) => System.Console.Write("Called4"); - public Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); - } + comp = CreateCompilationWithIL("", il); - class Test3 - { - public Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); - public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); - } + test1 = comp.GetMember("Params.Test1").Parameters.Last(); + test2 = comp.GetMember("Params.Test2").Parameters.Last(); - class Test4 - { - public Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); - public Test4(byte x, long y, long z) => System.Console.Write("Called9"); - } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); - CompileAndVerify( - comp, - expectedOutput: @"Called2Called1Called3Called5Called3Called4Called7Called6Called8Called9Called9"). - VerifyDiagnostics( - // (8,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test1(d1); // Called2 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test1(d1)").WithLocation(8, 9), - // (11,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test1(d2); // Called1 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test1(d2)").WithLocation(11, 9), - // (12,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test2(1, d1); // Called3 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test2(1, d1)").WithLocation(12, 9), - // (13,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test2(1, d2); // Called5 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test2(1, d2)").WithLocation(13, 9), - // (20,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test3(d3, 1, 2); // Called7 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test3(d3, 1, 2)").WithLocation(20, 9), - // (25,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test4(d3, x, x); // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test4(d3, x, x)").WithLocation(25, 9), - // (26,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test4(d3, d4, d4); // Called9 - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test4(d3, d4, d4)").WithLocation(26, 9) - ); - } + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - [Fact] - public void DynamicInvocation_Constructor_04() - { - var src = """ -using System.Collections.Generic; + AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + var src = @" class Program { static void Main() { - dynamic d = 1; - new Test(d, 2); - } - - class Test - { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called {0}", b is not null); - } - - public Test(int a, System.DateTime b) - { - } + Params.Test1(1); + Params.Test2(2); } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - - CompileAndVerify( - comp, - expectedOutput: @"Called True").VerifyDiagnostics(); +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_Constructor_05() + public void MetadataImport_03_Method() { - var src = """ -using System.Collections.Generic; - -class Program + // public class Params + // { + // static public void Test1([ParamArrayAttribute, ParamCollectionAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } + // static public void Test2([ParamArrayAttribute, ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } + // } + string il = @" +.class public auto ansi beforefieldinit Params + extends [mscorlib]System.Object { - static void Main() + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed { - dynamic d = 1; - new Test(d, 2, 3); + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - class Test + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed { - public Test(params IEnumerable b) - { - System.Console.Write("Called"); - } - } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); - } + .maxstack 8 - [Fact] - public void DynamicInvocation_Constructor_14_DoNotFilterBasedOnBetterFunctionMember() - { - var src = """ -using System.Collections.Generic; + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } -class Program -{ - static void Main() + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - dynamic d = 1L; - new Test(1, d, 2); + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } } -class Test +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute { - public Test(long a1, params IEnumerable a2) + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - System.Console.Write("long"); - } + .maxstack 8 - public Test(int b1, int b2, int b3) - { - System.Console.Write("int"); + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); +"; - comp.VerifyDiagnostics( - // (8,9): warning CS9505: One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - // new Test(1, d, 2); - Diagnostic(ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor, "new Test(1, d, 2)").WithLocation(8, 9) - ); - } + var comp = CreateCompilationWithIL("", il); - [Fact] - public void DynamicInvocation_Constructor_16_Abstract() - { - var src = """ -using System.Collections.Generic; + var test1 = comp.GetMember("Params.Test1").Parameters.Last(); + var test2 = comp.GetMember("Params.Test2").Parameters.Last(); + + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); + + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); + + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); + + var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; + AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + + comp = CreateCompilationWithIL("", il); + test1 = comp.GetMember("Params.Test1").Parameters.Last(); + test2 = comp.GetMember("Params.Test2").Parameters.Last(); + + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); + + VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); + VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); + + AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + + var src = @" class Program { static void Main() { - dynamic d = 1; - new Test(d); - } - - abstract class Test - { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called"); - } + Params.Test1(1); + Params.Test2(2); } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - - comp.VerifyDiagnostics( - // (8,9): error CS0144: Cannot create an instance of the abstract type or interface 'Program.Test' - // new Test(d); - Diagnostic(ErrorCode.ERR_NoNewAbstract, "new Test(d)").WithArguments("Program.Test").WithLocation(8, 9) - ); +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] - public void DynamicInvocation_ConstructorInitializer_01() + public void MetadataImport_04_Method() { - var src = """ -using System.Collections.Generic; - -class Program + // public class Params + // { + // static public void Test1([ParamArrayAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } + // static public void Test2([ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } + // } + string il = @" +.class public auto ansi beforefieldinit Params + extends [mscorlib]System.Object { - static void Main() + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed { - dynamic d = 1; - new C01(d); - new C02(d); - new C03(d); - new C04(d); - new C05(d); - new C06(d); + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) - new C07(d); - new C09(d); - new C10(d); - new C11(d); - new C12(d); - new C13(d); + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - class C01(dynamic d) : Test(d); - class C02(dynamic d) : Test(d, 1); - class C03(dynamic d) : Test(d, 2, 3); - class C04(dynamic d) : Test(2, d, 3); - class C05(dynamic d) : Test(2, 3, d); - class C06(dynamic d) : Test(d, [3, 4]); + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) - class C07(dynamic d) : Test2(d); - class C09(dynamic d) : Test2(d, 1); - class C10(dynamic d) : Test2(d, 2, 3); - class C11(dynamic d) : Test2(2, d, 3); - class C12(dynamic d) : Test2(2, 3, d); - class C13(dynamic d) : Test2(d, [3, 4]); + .maxstack 8 - class Test - { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called"); - } + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - class Test2 + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - public Test2(int a, params int[] b) - { - System.Console.Write("Called2"); - } + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"CalledCalledCalledCalledCalledCalledCalled2Called2Called2Called2Called2Called2").VerifyDiagnostics(); - } - - [Fact] - public void DynamicInvocation_ConstructorInitializer_02_AmbiguousDynamicParamsArgument() - { - var src = """ -using System.Collections.Generic; -class Program +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute { - static void Main() + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - dynamic d = 1; - new C(d); - } - - class C(dynamic d) : Test(d); + .maxstack 8 - class Test - { - public Test(params IEnumerable b) - { - System.Console.Write("Called"); - } + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); +"; - comp.VerifyDiagnostics( - // (11,30): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C(dynamic d) : Test(d); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d)").WithLocation(11, 30) - ); - } + var comp = CreateCompilationWithIL("", il); - [Fact] - public void DynamicInvocation_ConstructorInitializer_03_MultipleCandidates() - { - var src = """ -using System.Collections.Generic; + var test1 = comp.GetMember("Params.Test1").Parameters.Last(); + var test2 = comp.GetMember("Params.Test2").Parameters.Last(); -class Program -{ - static void Main() - { - dynamic d1 = System.DateTime.Now; - new C01(d1); - - dynamic d2 = new[] { 1 }; - new C02(d2); - new C03(d1); - new C04(d2); - - int x = 1; - new C05(x, d1); - new C06(x, d2); + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); - dynamic d3 = (byte)1; - new C07(d3); - new C08(d3, x); + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); - dynamic d4 = x; - new C09(d3, x); - new C10(d3, x); - new C11(d3, d4); - } + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); - class C01(dynamic d1) : Test1(d1); - - class C02(dynamic d2) : Test1(d2); - class C03(dynamic d1) : Test2(1, d1); - class C04(dynamic d2) : Test2(1, d2); - - class C05(int x, dynamic d1) : Test2(x, d1); - class C06(int x, dynamic d2) : Test2(x, d2); + AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); - class C07(dynamic d3) : Test3(d3, 1, 2); - class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 + comp = CreateCompilationWithIL("", il); - class C09(dynamic d3, int x) : Test4((byte)d3, x, x); // Called8 - class C10(dynamic d3, int x) : Test4(d3, x, x); - class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); + test1 = comp.GetMember("Params.Test1").Parameters.Last(); + test2 = comp.GetMember("Params.Test2").Parameters.Last(); - class Test1 - { - public Test1(params IEnumerable b) => System.Console.Write("Called1"); - public Test1(System.DateTime b) => System.Console.Write("Called2"); - } + Assert.Empty(test1.GetAttributes()); + Assert.Empty(test2.GetAttributes()); - class Test2 - { - public Test2(int x, System.DateTime b) => System.Console.Write("Called3"); - public Test2(long x, IEnumerable b) => System.Console.Write("Called4"); - public Test2(byte x, params IEnumerable b) => System.Console.Write("Called5"); - } + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); - class Test3 - { - public Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); - public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); - } + AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); - class Test4 + var src = @" +class Program +{ + static void Main() { - public Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); - public Test4(byte x, long y, long z) => System.Console.Write("Called9"); + Params.Test1(1); + Params.Test2(2); } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( - // (29,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C01(dynamic d1) : Test1(d1); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d1)").WithLocation(29, 34), - // (31,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C02(dynamic d2) : Test1(d2); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d2)").WithLocation(31, 34), - // (32,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C03(dynamic d1) : Test2(1, d1); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d1)").WithLocation(32, 34), - // (33,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C04(dynamic d2) : Test2(1, d2); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d2)").WithLocation(33, 34), - // (35,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C05(int x, dynamic d1) : Test2(x, d1); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d1)").WithLocation(35, 41), - // (36,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C06(int x, dynamic d2) : Test2(x, d2); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(x, d2)").WithLocation(36, 41), - // (38,34): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C07(dynamic d3) : Test3(d3, 1, 2); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, 1, 2)").WithLocation(38, 34), - // (42,41): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C10(dynamic d3, int x) : Test4(d3, x, x); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, x, x)").WithLocation(42, 41), - // (43,46): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C11(dynamic d3, dynamic d4) : Test4(d3, d4, d4); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(d3, d4, d4)").WithLocation(43, 46) + // (6,22): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // Params.Test1(1); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 22), + // (7,22): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + // Params.Test2(2); + Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 22) ); } [Fact] - public void DynamicInvocation_ConstructorInitializer_04() + public void MetadataImport_05_Property() { - var src = """ -using System.Collections.Generic; - -class Program + // public class Params1 + // { + // public int this[params System.Collections.Generic.IEnumerable a] + // { + // get + // { System.Console.Write("Test1"); return 0; } + // } + // } + // public class Params2 + // { + // public int this[params long[] a] + // { + // get + // { System.Console.Write("Test2"); return 0; } + // } + // } + string il = @" +.class public auto ansi beforefieldinit Params1 + extends [mscorlib]System.Object { - static void Main() - { - dynamic d = 1; - new C(d); - } - - class C(dynamic d) : Test(d, 2); + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) - class Test + .method public hidebysig specialname + instance int32 get_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called {0}", b is not null); - } + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 - public Test(int a, System.DateTime b) - { - } + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: ret } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called True").VerifyDiagnostics(); - } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 - [Fact] - public void DynamicInvocation_ConstructorInitializer_05() - { - var src = """ -using System.Collections.Generic; + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } -class Program -{ - static void Main() + .property instance int32 Item( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) { - dynamic d = 1; - new C(d); + .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) } +} - class C(dynamic d) : Test(d, 2, 3); +.class public auto ansi beforefieldinit Params2 + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) - class Test + .method public hidebysig specialname + instance int32 get_Item ( + int64[] a + ) cil managed { - public Test(params IEnumerable b) - { - System.Console.Write("Called"); - } + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: ret } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); - } + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 - [Fact] - public void DynamicInvocation_ConstructorInitializer_14_DoNotFilterBasedOnBetterFunctionMember() - { - var src = """ -using System.Collections.Generic; + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } -class Program -{ - static void Main() + .property instance int32 Item( + int64[] a + ) { - dynamic d = 1L; - new C(d); + .get instance int32 Params2::get_Item(int64[]) } -} -class C(dynamic d) : Test(1, d, 2); +} -class Test +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute { - public Test(long a1, params IEnumerable a2) + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - System.Console.Write("long"); - } + .maxstack 8 - public Test(int b1, int b2, int b3) - { - System.Console.Write("int"); + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } } -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); +"; - comp.VerifyDiagnostics( - // (12,26): error CS1975: The constructor call needs to be dynamically dispatched, but cannot be because it is part of a constructor initializer. Consider casting the dynamic arguments. - // class C(dynamic d) : Test(1, d, 2); - Diagnostic(ErrorCode.ERR_NoDynamicPhantomOnBaseCtor, "(1, d, 2)").WithLocation(12, 26) - ); - } + var comp = CreateCompilationWithIL("", il); - [Fact] - public void ExpressionTree() - { - var src = @" -using System.Linq.Expressions; + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); + + VerifyParams(test1, isParamCollection: true); + VerifyParams(test2, isParamArray: true); + var src = @" class Program { static void Main() { - Expression e1 = () => Test(); - Expression e2 = () => Test(1); - Expression e3 = () => Test(2, 3); - Expression e4 = () => Test([]); - } - - static void Test(params System.Collections.Generic.IEnumerable a) - { + _ = new Params1()[1]; + _ = new Params2()[2]; } } "; - var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - - // PROTOTYPE(ParamsCollections): report more specific error. - comp.VerifyDiagnostics( - // (8,46): error CS9175: An expression tree may not contain a collection expression. - // Expression e1 = () => Test(); - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test()").WithLocation(8, 46), - // (9,46): error CS9175: An expression tree may not contain a collection expression. - // Expression e2 = () => Test(1); - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test(1)").WithLocation(9, 46), - // (10,46): error CS9175: An expression tree may not contain a collection expression. - // Expression e3 = () => Test(2, 3); - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "Test(2, 3)").WithLocation(10, 46), - // (11,51): error CS9175: An expression tree may not contain a collection expression. - // Expression e4 = () => Test([]); - Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[]").WithLocation(11, 51) - ); + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] - public void MetadataImport_01_Method() + public void MetadataImport_06_Property() { - // public class Params + // public class Params1 // { - // static public void Test1(params System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } - // static public void Test2(params long[] a) { System.Console.Write("Test2"); } + // public int this[System.Collections.Generic.IEnumerable a] + // { + // [ParamCollectionAttribute, ParamArrayAttribute] get + // { System.Console.Write("Test1"); return 0; } + // } + // } + // public class Params2 + // { + // public int this[long[] a] + // { + // [ParamCollectionAttribute, ParamArrayAttribute] get + // { System.Console.Write("Test2"); return 0; } + // } // } string il = @" -.class public auto ansi beforefieldinit Params +.class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { - .method public hidebysig static - void Test1 ( + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) cil managed { @@ -6133,29 +8023,60 @@ .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) - + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } - .method public hidebysig static - void Test2 ( + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } + + .property instance int32 Item( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) + { + .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + } +} + +.class public auto ansi beforefieldinit Params2 + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( int64[] a ) cil managed { .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } .method public hidebysig specialname rtspecialname @@ -6167,6 +8088,14 @@ .maxstack 8 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } + + .property instance int32 Item( + int64[] a + ) + { + .get instance int32 Params2::get_Item(int64[]) + } + } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute @@ -6186,42 +8115,19 @@ .maxstack 8 var comp = CreateCompilationWithIL("", il); - var test1 = comp.GetMember("Params.Test1").Parameters.Last(); - var test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); - - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); - - comp = CreateCompilationWithIL("", il); - - test1 = comp.GetMember("Params.Test1").Parameters.Last(); - test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + VerifyParams(test1, isParamArray: true, isParamCollection: true); + VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { - Params.Test1(1); - Params.Test2(2); + _ = new Params1()[1]; + _ = new Params2()[2]; } } "; @@ -6230,55 +8136,95 @@ static void Main() } [Fact] - public void MetadataImport_02_Method() + public void MetadataImport_07_Property() { - // public class Params + // public class Params1 // { - // static public void Test1([ParamCollectionAttribute, ParamArrayAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } - // static public void Test2([ParamCollectionAttribute, ParamArrayAttribute] long[] a) { System.Console.Write("Test2"); } + // public int this[System.Collections.Generic.IEnumerable a] + // { + // [ParamArrayAttribute, ParamCollectionAttribute] get + // { System.Console.Write("Test1"); return 0; } + // } + // } + // public class Params2 + // { + // public int this[long[] a] + // { + // [ParamArrayAttribute, ParamCollectionAttribute] get + // { System.Console.Write("Test2"); return 0; } + // } // } string il = @" -.class public auto ansi beforefieldinit Params +.class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { - .method public hidebysig static - void Test1 ( + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) cil managed { .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } - .method public hidebysig static - void Test2 ( + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } + + .property instance int32 Item( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) + { + .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + } +} + +.class public auto ansi beforefieldinit Params2 + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( int64[] a ) cil managed { .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } .method public hidebysig specialname rtspecialname @@ -6290,6 +8236,14 @@ .maxstack 8 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } + + .property instance int32 Item( + int64[] a + ) + { + .get instance int32 Params2::get_Item(int64[]) + } + } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute @@ -6298,54 +8252,30 @@ extends [mscorlib]System.Attribute .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret - } -} -"; - - var comp = CreateCompilationWithIL("", il); - - var test1 = comp.GetMember("Params.Test1").Parameters.Last(); - var test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - - var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; - AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - - comp = CreateCompilationWithIL("", il); + .maxstack 8 - test1 = comp.GetMember("Params.Test1").Parameters.Last(); - test2 = comp.GetMember("Params.Test2").Parameters.Last(); + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret + } +} +"; - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); + var comp = CreateCompilationWithIL("", il); - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + VerifyParams(test1, isParamArray: true, isParamCollection: true); + VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { - Params.Test1(1); - Params.Test2(2); + _ = new Params1()[1]; + _ = new Params2()[2]; } } "; @@ -6354,19 +8284,34 @@ static void Main() } [Fact] - public void MetadataImport_03_Method() + public void MetadataImport_08_Property() { - // public class Params + // public class Params1 // { - // static public void Test1([ParamArrayAttribute, ParamCollectionAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } - // static public void Test2([ParamArrayAttribute, ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } + // public int this[System.Collections.Generic.IEnumerable a] + // { + // [ParamArrayAttribute] get + // { System.Console.Write("Test1"); return 0; } + // } + // } + // public class Params2 + // { + // public int this[long[] a] + // { + // [ParamCollectionAttribute] get + // { System.Console.Write("Test2"); return 0; } + // } // } string il = @" -.class public auto ansi beforefieldinit Params +.class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { - .method public hidebysig static - void Test1 ( + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) cil managed { @@ -6374,35 +8319,54 @@ .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } - .method public hidebysig static - void Test2 ( + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } + + .property instance int32 Item( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) + { + .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + } +} + +.class public auto ansi beforefieldinit Params2 + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance int32 get_Item ( int64[] a ) cil managed { .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + IL_000a: ldc.i4.0 + IL_000b: ret } .method public hidebysig specialname rtspecialname @@ -6414,6 +8378,14 @@ .maxstack 8 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } + + .property instance int32 Item( + int64[] a + ) + { + .get instance int32 Params2::get_Item(int64[]) + } + } .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute @@ -6433,72 +8405,70 @@ .maxstack 8 var comp = CreateCompilationWithIL("", il); - var test1 = comp.GetMember("Params.Test1").Parameters.Last(); - var test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); - - var attributes = new[] { "System.ParamArrayAttribute", "System.Runtime.CompilerServices.ParamCollectionAttribute" }; - AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - - comp = CreateCompilationWithIL("", il); - - test1 = comp.GetMember("Params.Test1").Parameters.Last(); - test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamArray: true, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true, isParamCollection: true); + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - AssertEx.Equal(attributes, test1.GetCustomAttributesToEmit(null).Select(a => a.ToString())); - AssertEx.Equal(attributes, test2.GetCustomAttributesToEmit(null).Select(a => a.ToString())); + VerifyParams(test1, isParamArray: true); + VerifyParams(test2, isParamCollection: true); var src = @" class Program { static void Main() { - Params.Test1(1); - Params.Test2(2); + _ = new Params1()[1]; + _ = new Params2()[2]; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // _ = new Params1()[1]; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 27), + // (7,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + // _ = new Params2()[2]; + Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 27) + ); } [Fact] - public void MetadataImport_04_Method() + public void MetadataImport_09_Property() { - // public class Params + // public class Params1 // { - // static public void Test1([ParamArrayAttribute] System.Collections.Generic.IEnumerable a) { System.Console.Write("Test1"); } - // static public void Test2([ParamCollectionAttribute] long[] a) { System.Console.Write("Test2"); } + // public int this[params System.Collections.Generic.IEnumerable a] + // { + // set + // { System.Console.Write("Test1"); } + // } + // } + // public class Params2 + // { + // public int this[params long[] a] + // { + // set + // { System.Console.Write("Test2"); } + // } // } string il = @" -.class public auto ansi beforefieldinit Params +.class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { - .method public hidebysig static - void Test1 ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance void set_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a, + int32 'value' ) cil managed { .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test1"" @@ -6506,16 +8476,41 @@ .maxstack 8 IL_000a: ret } - .method public hidebysig static - void Test2 ( - int64[] a + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } + + .property instance int32 Item( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) + { + .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) + } +} + +.class public auto ansi beforefieldinit Params2 + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + + .method public hidebysig specialname + instance void set_Item ( + int64[] a, + int32 'value' ) cil managed { .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .maxstack 8 IL_0000: ldstr ""Test2"" @@ -6532,92 +8527,69 @@ .maxstack 8 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } -} -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute -{ - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + .property instance int32 Item( + int64[] a + ) { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + .set instance void Params2::set_Item(int64[], int32) } -} -"; - - var comp = CreateCompilationWithIL("", il); - - var test1 = comp.GetMember("Params.Test1").Parameters.Last(); - var test2 = comp.GetMember("Params.Test2").Parameters.Last(); - - VerifyParamsAndAttribute(test1, isParamArray: true); - VerifyParamsAndAttribute(test2, isParamCollection: true); - - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); - - VerifyParamsAndAttribute(test1, isParamArray: true); - VerifyParamsAndAttribute(test2, isParamCollection: true); - - AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); - comp = CreateCompilationWithIL("", il); +} +.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute + extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 - test1 = comp.GetMember("Params.Test1").Parameters.Last(); - test2 = comp.GetMember("Params.Test2").Parameters.Last(); + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret + } +} +"; - Assert.Empty(test1.GetAttributes()); - Assert.Empty(test2.GetAttributes()); + var comp = CreateCompilationWithIL("", il); - VerifyParamsAndAttribute(test1, isParamArray: true); - VerifyParamsAndAttribute(test2, isParamCollection: true); + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + VerifyParams(test1, isParamCollection: true); + VerifyParams(test2, isParamArray: true); var src = @" class Program { static void Main() { - Params.Test1(1); - Params.Test2(2); + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (6,22): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' - // Params.Test1(1); - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 22), - // (7,22): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' - // Params.Test2(2); - Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 22) - ); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] - public void MetadataImport_05_Property() + public void MetadataImport_10_Property() { // public class Params1 // { - // public int this[params System.Collections.Generic.IEnumerable a] + // public int this[System.Collections.Generic.IEnumerable a] // { - // get - // { System.Console.Write("Test1"); return 0; } + // [ParamCollectionAttribute, ParamArrayAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { - // public int this[params long[] a] + // public int this[long[] a] // { - // get - // { System.Console.Write("Test2"); return 0; } + // [ParamCollectionAttribute, ParamArrayAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6629,20 +8601,23 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a + instance void set_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a, + int32 'value' ) cil managed { .param [1] .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6659,7 +8634,7 @@ .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) { - .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) } } @@ -6671,11 +8646,15 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - int64[] a + instance void set_Item ( + int64[] a, + int32 'value' ) cil managed { .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) @@ -6683,8 +8662,7 @@ .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6701,11 +8679,10 @@ .property instance int32 Item( int64[] a ) { - .get instance int32 Params2::get_Item(int64[]) + .set instance void Params2::set_Item(int64[], int32) } } - .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -6726,16 +8703,16 @@ .maxstack 8 var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - VerifyParams(test1, isParamCollection: true); - VerifyParams(test2, isParamArray: true); + VerifyParams(test1, isParamArray: true, isParamCollection: true); + VerifyParams(test2, isParamArray: true, isParamCollection: true); var src = @" class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; @@ -6744,22 +8721,22 @@ static void Main() } [Fact] - public void MetadataImport_06_Property() + public void MetadataImport_11_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamCollectionAttribute, ParamArrayAttribute] get - // { System.Console.Write("Test1"); return 0; } + // [ParamArrayAttribute, ParamCollectionAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamCollectionAttribute, ParamArrayAttribute] get - // { System.Console.Write("Test2"); return 0; } + // [ParamArrayAttribute, ParamCollectionAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6771,23 +8748,23 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a + instance void set_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a, + int32 'value' ) cil managed { .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6804,7 +8781,7 @@ .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) { - .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) } } @@ -6816,23 +8793,23 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - int64[] a + instance void set_Item ( + int64[] a, + int32 'value' ) cil managed { .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6849,11 +8826,10 @@ .property instance int32 Item( int64[] a ) { - .get instance int32 Params2::get_Item(int64[]) + .set instance void Params2::set_Item(int64[], int32) } } - .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -6882,8 +8858,8 @@ class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; @@ -6892,22 +8868,22 @@ static void Main() } [Fact] - public void MetadataImport_07_Property() + public void MetadataImport_12_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamArrayAttribute, ParamCollectionAttribute] get - // { System.Console.Write("Test1"); return 0; } + // [ParamArrayAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamArrayAttribute, ParamCollectionAttribute] get - // { System.Console.Write("Test2"); return 0; } + // [ParamCollectionAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6919,23 +8895,20 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a + instance void set_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a, + int32 'value' ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6952,7 +8925,7 @@ .property instance int32 Item( class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) { - .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) } } @@ -6964,14 +8937,12 @@ 01 00 04 49 74 65 6d 00 00 ) .method public hidebysig specialname - instance int32 get_Item ( - int64[] a + instance void set_Item ( + int64[] a, + int32 'value' ) cil managed { .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) @@ -6979,8 +8950,7 @@ .maxstack 8 IL_0000: ldstr ""Test2"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -6997,11 +8967,10 @@ .property instance int32 Item( int64[] a ) { - .get instance int32 Params2::get_Item(int64[]) + .set instance void Params2::set_Item(int64[], int32) } } - .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -7022,40 +8991,100 @@ .maxstack 8 var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - VerifyParams(test1, isParamArray: true, isParamCollection: true); - VerifyParams(test2, isParamArray: true, isParamCollection: true); + VerifyParams(test1, isParamArray: true); + VerifyParams(test2, isParamCollection: true); var src = @" class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (6,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // new Params1()[1] = 0; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 23), + // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + // new Params2()[2] = 0; + Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 23) + ); } - [Fact] - public void MetadataImport_08_Property() + [Flags] + public enum ParamsAttributes + { + None = 0, + Array = 1, + Collection = 2, + Both = Array | Collection, + } + + private string GetAttributesIL(ParamsAttributes attributes) + { + if (attributes == ParamsAttributes.None) + { + return ""; + } + + string result = @" .param [1] +"; + + if ((attributes & ParamsAttributes.Array) != 0) + { + result += @" + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) +"; + } + + if ((attributes & ParamsAttributes.Collection) != 0) + { + result += @" + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) +"; + } + + return result; + } + + [Theory] + [CombinatorialData] + public void MetadataImport_13_Property(ParamsAttributes getAttributes, ParamsAttributes setAttributes) { + if (getAttributes == setAttributes) + { + return; + } + + var getAttributesString = GetAttributesIL(getAttributes); + var setAttributesString = GetAttributesIL(setAttributes); + // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamArrayAttribute] get + // [getAttributes] get // { System.Console.Write("Test1"); return 0; } + // [setAttributes] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamCollectionAttribute] get + // [getAttributes] get // { System.Console.Write("Test2"); return 0; } + // [setAttributes] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -7066,21 +9095,33 @@ extends [mscorlib]System.Object 01 00 04 49 74 65 6d 00 00 ) + + .method public hidebysig specialname + instance int32 get_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed + { + " + getAttributesString + @" + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: ret + } + .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a + instance void set_Item ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a, + int32 'value' ) cil managed { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) + " + setAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + IL_000a: ret } .method public hidebysig specialname rtspecialname @@ -7098,6 +9139,7 @@ class [mscorlib]System.Collections.Generic.IEnumerable`1 a ) { .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) + .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) } } @@ -7108,15 +9150,13 @@ extends [mscorlib]System.Object 01 00 04 49 74 65 6d 00 00 ) + .method public hidebysig specialname instance int32 get_Item ( int64[] a ) cil managed { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) + " + getAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test2"" @@ -7125,6 +9165,20 @@ .maxstack 8 IL_000b: ret } + .method public hidebysig specialname + instance void set_Item ( + int64[] a, + int32 'value' + ) cil managed + { + " + setAttributesString + @" + .maxstack 8 + + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { @@ -7140,10 +9194,10 @@ int64[] a ) { .get instance int32 Params2::get_Item(int64[]) + .set instance void Params2::set_Item(int64[], int32) } } - .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -7159,14 +9213,6 @@ .maxstack 8 } "; - var comp = CreateCompilationWithIL("", il); - - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - - VerifyParams(test1, isParamArray: true); - VerifyParams(test2, isParamCollection: true); - var src = @" class Program { @@ -7174,39 +9220,63 @@ static void Main() { _ = new Params1()[1]; _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; - comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); + + VerifyParams(test1, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); + VerifyParams(test2, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); + + string getModifier = getAttributes == ParamsAttributes.None ? "" : "params "; + string setModifier = setAttributes == ParamsAttributes.None ? "" : "params "; + comp.VerifyDiagnostics( - // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // (6,13): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable)' or 'Params1.set_Item(params IEnumerable, int)' // _ = new Params1()[1]; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 27), - // (7,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable, int)").WithLocation(6, 13), + // (7,13): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' // _ = new Params2()[2]; - Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 27) + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(7, 13), + // (8,9): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable)' or 'Params1.set_Item(params IEnumerable, int)' + // new Params1()[1] = 0; + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable, int)").WithLocation(8, 9), + // (9,9): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' + // new Params2()[2] = 0; + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(9, 9) ); } - [Fact] - public void MetadataImport_09_Property() + [Theory] + [CombinatorialData] + public void MetadataImport_14_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) { + if (parameterAttributes == ParamsAttributes.None || + parameterType is not (ParamsAttributes.Array or ParamsAttributes.Collection) || + (parameterAttributes & parameterType) == 0) + { + return; + } + + var attributesString = GetAttributesIL(parameterAttributes); + bool isArrayType = parameterType == ParamsAttributes.Array; + var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1"; + // public class Params1 // { - // public int this[params System.Collections.Generic.IEnumerable a] + // public int this[System.Collections.Generic.IEnumerable or long[] a] // { - // set + // [parameterAttributes] get + // { System.Console.Write("Test1"); return 0; } + // [parameterAttributes] set // { System.Console.Write("Test1"); } // } // } - // public class Params2 - // { - // public int this[params long[] a] - // { - // set - // { System.Console.Write("Test2"); } - // } - // } string il = @" .class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object @@ -7215,61 +9285,31 @@ extends [mscorlib]System.Object 01 00 04 49 74 65 6d 00 00 ) + .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' + instance int32 get_Item ( + " + typeString + @" a ) cil managed { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) + " + attributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } - - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) - { - .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) + IL_000a: ldc.i4.0 + IL_000b: ret } -} - -.class public auto ansi beforefieldinit Params2 - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) .method public hidebysig specialname instance void set_Item ( - int64[] a, + " + typeString + @" a, int32 'value' ) cil managed { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) + " + attributesString + @" .maxstack 8 - IL_0000: ldstr ""Test2"" + IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } @@ -7285,13 +9325,14 @@ .maxstack 8 } .property instance int32 Item( - int64[] a + " + typeString + @" a ) { - .set instance void Params2::set_Item(int64[], int32) + .get instance int32 Params1::get_Item(" + typeString + @") + .set instance void Params1::set_Item(" + typeString + @", int32) } - } + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -7307,116 +9348,85 @@ .maxstack 8 } "; - var comp = CreateCompilationWithIL("", il); - - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - - VerifyParams(test1, isParamCollection: true); - VerifyParams(test2, isParamArray: true); - var src = @" class Program { static void Main() { + _ = new Params1()[1]; new Params1()[1] = 0; - new Params2()[2] = 0; } } "; - comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); - } - - [Fact] - public void MetadataImport_10_Property() - { - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable a] - // { - // [ParamCollectionAttribute, ParamArrayAttribute] set - // { System.Console.Write("Test1"); } - // } - // } - // public class Params2 - // { - // public int this[long[] a] - // { - // [ParamCollectionAttribute, ParamArrayAttribute] set - // { System.Console.Write("Test2"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' - ) cil managed - { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } + VerifyParams(test1, isParamArray: (parameterAttributes & ParamsAttributes.Array) != 0, isParamCollection: (parameterAttributes & ParamsAttributes.Collection) != 0); - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + CompileAndVerify(comp, expectedOutput: "Test1Test1").VerifyDiagnostics(); + } - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + [Theory] + [CombinatorialData] + public void MetadataImport_15_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) + { + switch (parameterType, parameterAttributes) + { + case (ParamsAttributes.Array, ParamsAttributes.Collection): + case (ParamsAttributes.Collection, ParamsAttributes.Array): + break; + default: + return; + } - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) - { - .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) - } -} + var attributesString = GetAttributesIL(parameterAttributes); + bool isArrayType = parameterType == ParamsAttributes.Array; + var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1"; -.class public auto ansi beforefieldinit Params2 + // public class Params1 + // { + // public int this[System.Collections.Generic.IEnumerable or long[] a] + // { + // [parameterAttributes] get + // { System.Console.Write("Test1"); return 0; } + // [parameterAttributes] set + // { System.Console.Write("Test1"); } + // } + // } + string il = @" +.class public auto ansi beforefieldinit Params1 extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) + + .method public hidebysig specialname + instance int32 get_Item ( + " + typeString + @" a + ) cil managed + { + " + attributesString + @" + .maxstack 8 + + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: ret + } + .method public hidebysig specialname instance void set_Item ( - int64[] a, + " + typeString + @" a, int32 'value' ) cil managed { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) + " + attributesString + @" .maxstack 8 - IL_0000: ldstr ""Test2"" + IL_0000: ldstr ""Test1"" IL_0005: call void [mscorlib]System.Console::Write(string) IL_000a: ret } @@ -7432,13 +9442,14 @@ .maxstack 8 } .property instance int32 Item( - int64[] a + " + typeString + @" a ) { - .set instance void Params2::set_Item(int64[], int32) + .get instance int32 Params1::get_Item(" + typeString + @") + .set instance void Params1::set_Item(" + typeString + @", int32) } - } + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -7454,138 +9465,158 @@ .maxstack 8 } "; - var comp = CreateCompilationWithIL("", il); - - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); - - VerifyParams(test1, isParamArray: true, isParamCollection: true); - VerifyParams(test2, isParamArray: true, isParamCollection: true); - var src = @" class Program { static void Main() { + _ = new Params1()[1]; new Params1()[1] = 0; - new Params2()[2] = 0; } } "; - comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + + var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + + VerifyParams(test1, isParamArray: parameterAttributes == ParamsAttributes.Array, isParamCollection: parameterAttributes == ParamsAttributes.Collection); + + if (isArrayType) + { + comp.VerifyDiagnostics( + // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + // _ = new Params1()[1]; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(6, 27), + // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' + // new Params1()[1] = 0; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(7, 23) + ); + } + else + { + comp.VerifyDiagnostics( + // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // _ = new Params1()[1]; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 27), + // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' + // new Params1()[1] = 0; + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(7, 23) + ); + } } - [Fact] - public void MetadataImport_11_Property() + [Fact(Skip = "Get rid of LocalRewriter.MakeCallWithNoExplicitArgument, it cannot handle params collections")] // PROTOTYPE(ParamsCollections): enable and test runtime behavior + public void UsingPatternWithParamsTest() { - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable a] - // { - // [ParamArrayAttribute, ParamCollectionAttribute] set - // { System.Console.Write("Test1"); } - // } - // } - // public class Params2 - // { - // public int this[long[] a] - // { - // [ParamArrayAttribute, ParamCollectionAttribute] set - // { System.Console.Write("Test2"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object + var source = @" +using System.Collections.Generic; + +ref struct S1 { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + public void Dispose(params IEnumerable args){ } +} - .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' - ) cil managed +class C2 +{ + static void Main() { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + using (S1 c = new S1()) + { + } + S1 c1b = new S1(); + using (c1b) { } } +}"; + CreateCompilation(source).VerifyEmitDiagnostics(); + } - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + [Fact(Skip = "Get rid of LocalRewriter.MakeCallWithNoExplicitArgument, it cannot handle params collections")] // PROTOTYPE(ParamsCollections): enable and test runtime behavior + public void UsingPatternWithParamsTest_Foreach() + { + var source = @" +using System.Collections.Generic; - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } +ref struct S1 +{ + public void Dispose(params IEnumerable args){ } + public int Current => 0; + public bool MoveNext() => false; +} - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) +class C2 +{ + public S1 GetEnumerator() => default; + + static void Main() { - .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) + foreach (var i in new C2()) + { + } } -} +}"; + CreateCompilation(source).VerifyEmitDiagnostics(); + } -.class public auto ansi beforefieldinit Params2 - extends [mscorlib]System.Object + [Fact] + public void ERR_ExplicitImplParams_01() + { + var source = @" +using System.Collections.Generic; + +interface I1 { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + void M1(params IEnumerable args); + void M2(IEnumerable args); + void M3(params IEnumerable args); +} +class C2 : I1 +{ + void I1.M1(IEnumerable args) {} + void I1.M2(params IEnumerable args) {} + void I1.M3(params IEnumerable args) {} +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,13): error CS0466: 'C2.I1.M2(params IEnumerable)' should not have a params parameter since 'I1.M2(IEnumerable)' does not + // void I1.M2(params IEnumerable args) {} + Diagnostic(ErrorCode.ERR_ExplicitImplParams, "M2").WithArguments("C2.I1.M2(params System.Collections.Generic.IEnumerable)", "I1.M2(System.Collections.Generic.IEnumerable)").WithLocation(13, 13) + ); + } - .method public hidebysig specialname - instance void set_Item ( - int64[] a, - int32 'value' + [Fact] + public void ERR_ExplicitImplParams_02() + { + // public interface I1 + // { + // void M1([ParamArrayAttribute] IEnumerable args); + // void M2([ParamCollectionAttribute] int[] args); + // } + var il = @" +.class interface public auto ansi abstract beforefieldinit I1 +{ + .method public hidebysig newslot abstract virtual + instance void M1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 args ) cil managed { .param [1] .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 - - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret } - .property instance int32 Item( - int64[] a - ) + .method public hidebysig newslot abstract virtual + instance void M2 ( + int32[] args + ) cil managed { - .set instance void Params2::set_Item(int64[], int32) + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) } - } + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute { @@ -7601,1384 +9632,1738 @@ .maxstack 8 } "; - var comp = CreateCompilationWithIL("", il); + var source = @" +using System.Collections.Generic; - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); +class C1 : I1 +{ + void I1.M1(IEnumerable args) {} + void I1.M2(int[] args) {} +} - VerifyParams(test1, isParamArray: true, isParamCollection: true); - VerifyParams(test2, isParamArray: true, isParamCollection: true); +class C2 : I1 +{ + void I1.M1(params IEnumerable args) {} + void I1.M2(params int[] args) {} +} +"; + CreateCompilationWithIL(source, il).VerifyDiagnostics(); + } + + [Fact] + public void EmbedAttribute_01_Constructor() + { + var src = """ +using System.Collections.Generic; - var src = @" class Program { - static void Main() + public Program(params IEnumerable x) { - new Params1()[1] = 0; - new Params2()[2] = 0; + } +} +"""; + VerifyAttributeEmbedding( + src, + "Program..ctor", + "Program..ctor(params System.Collections.Generic.IEnumerable x)", + // (5,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // public Program(params IEnumerable x) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 20) + ); + } + + private void VerifyAttributeEmbedding(string src, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) + { + VerifyAttributeEmbedding(src1: src, src2: null, memberName, memberDisplay, moduleDiagnostic); + } + + private void VerifyAttributeEmbedding(string src1, string src2, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) + { + IEnumerable references = src2 is null ? [] : [CreateCompilation(src2).ToMetadataReference()]; + + var comp = CreateCompilation(src1, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + verify(comp, attributeIsEmbedded: true); + + var comp1 = CreateCompilation(ParamCollectionAttributeSource, options: TestOptions.ReleaseDll); + var comp1Ref = comp1.ToMetadataReference(); + var comp2 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp2, attributeIsEmbedded: false); + Assert.Contains(comp1Ref, comp2.GetUsedAssemblyReferences()); + + var comp3 = CreateCompilation(src1, references, options: TestOptions.ReleaseModule); + comp3.VerifyEmitDiagnostics(moduleDiagnostic); + Assert.NotEmpty(moduleDiagnostic); + + var comp4 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseModule.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp4, attributeIsEmbedded: false); + Assert.Contains(comp1Ref, comp4.GetUsedAssemblyReferences()); + + const string brokenParamCollectionAttributeSource = @" +namespace System.Runtime.CompilerServices +{ + public sealed class ParamCollectionAttribute : Attribute + { + public ParamCollectionAttribute(int x) { } } } "; - comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + + var comp5 = CreateCompilation(brokenParamCollectionAttributeSource, options: TestOptions.ReleaseDll); + var comp5Ref = comp5.ToMetadataReference(); + var comp6 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseDll); + comp6.VerifyEmitDiagnostics( + // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' + // public Program(params IEnumerable x) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) + ); + + var comp7 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseModule); + comp7.VerifyEmitDiagnostics( + // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' + // public Program(params IEnumerable x) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) + ); + + void verify(CSharpCompilation comp, bool attributeIsEmbedded) + { + // We want to test attribute embedding + Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); + + CompileAndVerify( + comp, + symbolValidator: (m) => + { + string adjustedMemberName = memberName; + string adjustedMemberDisplay = memberDisplay; + if (comp.Options.OutputKind == OutputKind.NetModule && memberName.StartsWith("<>")) + { + adjustedMemberName = adjustedMemberName.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); + adjustedMemberDisplay = adjustedMemberDisplay.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); + } + + MethodSymbol member = m.GlobalNamespace.GetMember(adjustedMemberName); + AssertEx.Equal(adjustedMemberDisplay, member.ToTestDisplayString()); + VerifyParamsAndAttribute(member.Parameters[0], isParamCollection: true); + + if (member.AssociatedSymbol is PropertySymbol prop) + { + VerifyParams(prop.Parameters[0], isParamCollection: true); + } + + if (attributeIsEmbedded) + { + Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); + } + else if (!m.GlobalNamespace.GetMembers("System").IsEmpty) + { + Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + } + }, + verify: comp.Options.OutputKind != OutputKind.NetModule ? + Verification.Passes : + Verification.Fails with + { + PEVerifyMessage = "The module was expected to contain an assembly manifest.", + ILVerifyMessage = "The format of a DLL or executable being loaded is invalid" + } + ).VerifyDiagnostics(); + } } [Fact] - public void MetadataImport_12_Property() + public void EmbedAttribute_02_Delegate() { - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable a] - // { - // [ParamArrayAttribute] set - // { System.Console.Write("Test1"); } - // } - // } - // public class Params2 - // { - // public int this[long[] a] - // { - // [ParamCollectionAttribute] set - // { System.Console.Write("Test2"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + var src = """ +using System.Collections.Generic; - .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' - ) cil managed +delegate void Program(params IEnumerable x); +"""; + VerifyAttributeEmbedding( + src, + "Program.Invoke", + "void Program.Invoke(params System.Collections.Generic.IEnumerable x)", + // (3,23): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // delegate void Program(params IEnumerable x); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(3, 23) + ); + } + + [Fact] + public void EmbedAttribute_03_AnonymousDelegate_FromMember() + { + var src1 = @" +class Program +{ + static void Main() { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 + var x1 = Params.Test1; - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + x1(1); } +} +"; + var src2 = @" +using System.Collections.Generic; - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed +public class Params +{ + static public void Test1(params IEnumerable a) { } +} +"; + VerifyAttributeEmbedding( + src1, + src2, + "<>f__AnonymousDelegate0.Invoke", + "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", + // (6,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // var x1 = Params.Test1; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Params.Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(6, 18) + ); + } + + [Fact] + public void EmbedAttribute_04_AnonymousDelegate_FromLambda() + { + var src = @" +using System.Collections.Generic; + +class Program +{ + static void Main() { - .maxstack 8 + var x1 = (params IEnumerable a) => {}; - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret + x1(1); } +} +"; + VerifyAttributeEmbedding( + src, + "<>f__AnonymousDelegate0.Invoke", + "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", + // (8,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // var x1 = (params IEnumerable a) => {}; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable a").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(8, 19) + ); + } + + [Fact] + public void EmbedAttribute_05_AnonymousDelegate_FromLambda_ExpressionTree() + { + var src = @" +using System.Collections.Generic; +using System.Linq.Expressions; - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) +class Program +{ + static void Main() { - .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) + Test((params IEnumerable a) => {}); } + + static void Test(Expression e){} } +"; + CreateCompilation(src).VerifyEmitDiagnostics( + // (9,9): error CS0411: The type arguments for method 'Program.Test(Expression)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Test((params IEnumerable a) => {}); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Program.Test(System.Linq.Expressions.Expression)").WithLocation(9, 9) + ); + } -.class public auto ansi beforefieldinit Params2 - extends [mscorlib]System.Object -{ - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + [Fact] + public void EmbedAttribute_06_AnonymousDelegate_FromLocalFunction() + { + var src = """ +using System.Collections.Generic; - .method public hidebysig specialname - instance void set_Item ( - int64[] a, - int32 'value' - ) cil managed +class Program +{ + static void Main() { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - .maxstack 8 - - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } + var x1 = Test1; - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + x1(1); - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret + void Test1(params IEnumerable a) { } } +} +"""; + VerifyAttributeEmbedding( + src, + "<>f__AnonymousDelegate0.Invoke", + "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", + // (7,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // var x1 = Test1; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(7, 18) + ); + } - .property instance int32 Item( - int64[] a - ) + [Fact] + public void EmbedAttribute_07_RegularMethod() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + void Test(params IEnumerable x) { - .set instance void Params2::set_Item(int64[], int32) } - } -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute +"""; + VerifyAttributeEmbedding( + src, + "Program.Test", + "void Program.Test(params System.Collections.Generic.IEnumerable x)", + // (5,15): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // void Test(params IEnumerable x) + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 15) + ); + } + + [Fact] + public void EmbedAttribute_08_Operator() + { + var src = @" +using System.Collections.Generic; + +class Program { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + public static implicit operator Program(params List x) { - .maxstack 8 + return null; + } - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + public static Program operator +(Program x, params List y) + { + return null; } } "; + CreateCompilation(src).VerifyEmitDiagnostics( + // (6,45): error CS1670: params is not valid in this context + // public static implicit operator Program(params List x) + Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(6, 45), + // (11,49): error CS1670: params is not valid in this context + // public static Program operator +(Program x, params List y) + Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(11, 49) + ); + } - var comp = CreateCompilationWithIL("", il); + [Fact] + public void EmbedAttribute_09_Property_get() + { + var src = """ +using System.Collections.Generic; - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); +class Program +{ + int this[params IEnumerable x] + => 0; +} +"""; + VerifyAttributeEmbedding( + src, + "Program.get_Item", + "System.Int32 Program.this[params System.Collections.Generic.IEnumerable x].get", + // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int this[params IEnumerable x] + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) + ); + } - VerifyParams(test1, isParamArray: true); - VerifyParams(test2, isParamCollection: true); + [Fact] + public void EmbedAttribute_10_Property_get() + { + var src = """ +using System.Collections.Generic; - var src = @" class Program { - static void Main() - { - new Params1()[1] = 0; - new Params2()[2] = 0; - } + int this[params IEnumerable x] + { get => 0; set {} } } -"; - comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (6,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' - // new Params1()[1] = 0; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 23), - // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' - // new Params2()[2] = 0; - Diagnostic(ErrorCode.ERR_BadArgType, "2").WithArguments("1", "int", "params long[]").WithLocation(7, 23) +"""; + VerifyAttributeEmbedding( + src, + "Program.get_Item", + "System.Int32 Program.this[params System.Collections.Generic.IEnumerable x].get", + // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int this[params IEnumerable x] + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) ); } - [Flags] - public enum ParamsAttributes + [Fact] + public void EmbedAttribute_11_Property_set() { - None = 0, - Array = 1, - Collection = 2, - Both = Array | Collection, + var src = """ +using System.Collections.Generic; + +class Program +{ + int this[params IEnumerable x] + {set{}} +} +"""; + VerifyAttributeEmbedding( + src, + "Program.set_Item", + "void Program.this[params System.Collections.Generic.IEnumerable x].set", + // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int this[params IEnumerable x] + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) + ); } - private string GetAttributesIL(ParamsAttributes attributes) + [Fact] + public void EmbedAttribute_12_Property_set() { - if (attributes == ParamsAttributes.None) - { - return ""; - } + var src = """ +using System.Collections.Generic; + +class Program +{ + int this[params IEnumerable x] + {get => 0; set{}} +} +"""; + VerifyAttributeEmbedding( + src, + "Program.set_Item", + "void Program.this[params System.Collections.Generic.IEnumerable x].set", + // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int this[params IEnumerable x] + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) + ); + } + + [Fact] + public void ConsumeAcrossAssemblyBoundary_01_Method() + { + var src1 = """ +using System.Collections.Generic; +using System.Linq; + +public class Params +{ + public static void Test(params IEnumerable a) + { + var array = a.ToArray(); + if (array.Length == 0) + { + System.Console.WriteLine(array.Length); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); + } + } +} +"""; + var src2 = """ +class Program +{ + static void Main() + { + Params.Test(); + Params.Test(1); + Params.Test(2, 3); + } +} +"""; + var comp1 = CreateCompilation(src1); - string result = @" .param [1] -"; + verify(image: true); + verify(image: false); - if ((attributes & ParamsAttributes.Array) != 0) + void verify(bool image) { - result += @" - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) -"; - } + var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - if ((attributes & ParamsAttributes.Collection) != 0) - { - result += @" - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) -"; + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); } - - return result; } [Theory] [CombinatorialData] - public void MetadataImport_13_Property(ParamsAttributes getAttributes, ParamsAttributes setAttributes) + public void ConsumeAcrossAssemblyBoundary_02_Property(bool hasSet) { - if (getAttributes == setAttributes) - { - return; - } - - var getAttributesString = GetAttributesIL(getAttributes); - var setAttributesString = GetAttributesIL(setAttributes); + var src1 = """ +using System.Collections.Generic; +using System.Linq; - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable a] - // { - // [getAttributes] get - // { System.Console.Write("Test1"); return 0; } - // [setAttributes] set - // { System.Console.Write("Test1"); } - // } - // } - // public class Params2 - // { - // public int this[long[] a] - // { - // [getAttributes] get - // { System.Console.Write("Test2"); return 0; } - // [setAttributes] set - // { System.Console.Write("Test2"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object +public class Params { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) - - - .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) cil managed - { - " + getAttributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret - } - - .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' - ) cil managed - { - " + setAttributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + public int this[char c, params IEnumerable a] { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + get + { + var array = a.ToArray(); + if (array.Length == 0) + { + System.Console.WriteLine(array.Length); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); + } - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) - { - .get instance int32 Params1::get_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1) - .set instance void Params1::set_Item(class [mscorlib]System.Collections.Generic.IEnumerable`1, int32) + return 0; + } +""" + (hasSet ? """ + set {} +""" : +"") + + """ } } - -.class public auto ansi beforefieldinit Params2 - extends [mscorlib]System.Object +"""; + var src2 = """ +class Program { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) - - - .method public hidebysig specialname - instance int32 get_Item ( - int64[] a - ) cil managed + static void Main() { - " + getAttributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret + var p = new Params(); + _ = p['a']; + _ = p['b', 1]; + _ = p['c', 2, 3]; } +} +"""; + var comp1 = CreateCompilation(src1); - .method public hidebysig specialname - instance void set_Item ( - int64[] a, - int32 'value' - ) cil managed - { - " + setAttributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } + verify(image: true); + verify(image: false); - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + void verify(bool image) + { + var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + } + } - .property instance int32 Item( - int64[] a - ) - { - .get instance int32 Params2::get_Item(int64[]) - .set instance void Params2::set_Item(int64[], int32) - } + [Theory] + [CombinatorialData] + public void ConsumeAcrossAssemblyBoundary_03_Property(bool hasGet) + { + var src1 = """ +using System.Collections.Generic; +using System.Linq; -} -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute +public class Params { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + public int this[char c, params IEnumerable a] { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + set + { + var array = a.ToArray(); + if (array.Length == 0) + { + System.Console.WriteLine(array.Length); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); + } + } +""" + (hasGet ? """ + get => 0; +""" : +"") + + """ } } -"; - - var src = @" +"""; + var src2 = """ class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; - new Params1()[1] = 0; - new Params2()[2] = 0; + var p = new Params(); + p['a'] = 0; + p['b', 1] = 1; + p['c', 2, 3] = 2; } } -"; - var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); +"""; + var comp1 = CreateCompilation(src1); - VerifyParams(test1, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); - VerifyParams(test2, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); + verify(image: true); + verify(image: false); - string getModifier = getAttributes == ParamsAttributes.None ? "" : "params "; - string setModifier = setAttributes == ParamsAttributes.None ? "" : "params "; + void verify(bool image) + { + var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics( - // (6,13): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable)' or 'Params1.set_Item(params IEnumerable, int)' - // _ = new Params1()[1]; - Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable, int)").WithLocation(6, 13), - // (7,13): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' - // _ = new Params2()[2]; - Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(7, 13), - // (8,9): error CS1545: Property, indexer, or event 'Params1.this[params IEnumerable]' is not supported by the language; try directly calling accessor methods 'Params1.get_Item(params IEnumerable)' or 'Params1.set_Item(params IEnumerable, int)' - // new Params1()[1] = 0; - Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params1()[1]").WithArguments("Params1.this[" + setModifier + "System.Collections.Generic.IEnumerable]", "Params1.get_Item(" + getModifier + "System.Collections.Generic.IEnumerable)", "Params1.set_Item(" + setModifier + "System.Collections.Generic.IEnumerable, int)").WithLocation(8, 9), - // (9,9): error CS1545: Property, indexer, or event 'Params2.this[params long[]]' is not supported by the language; try directly calling accessor methods 'Params2.get_Item(params long[])' or 'Params2.set_Item(params long[], int)' - // new Params2()[2] = 0; - Diagnostic(ErrorCode.ERR_BindToBogusProp2, "new Params2()[2]").WithArguments("Params2.this[" + setModifier + "long[]]", "Params2.get_Item(" + getModifier + "long[])", "Params2.set_Item(" + setModifier + "long[], int)").WithLocation(9, 9) - ); + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + } } - [Theory] - [CombinatorialData] - public void MetadataImport_14_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] + public void UnsafeContext_01_Constructor() { - if (parameterAttributes == ParamsAttributes.None || - parameterType is not (ParamsAttributes.Array or ParamsAttributes.Collection) || - (parameterAttributes & parameterType) == 0) - { - return; - } - - var attributesString = GetAttributesIL(parameterAttributes); - bool isArrayType = parameterType == ParamsAttributes.Array; - var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1"; - - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable or long[] a] - // { - // [parameterAttributes] get - // { System.Console.Write("Test1"); return 0; } - // [parameterAttributes] set - // { System.Console.Write("Test1"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object + string source1 = """ +using System.Collections; +using System.Collections.Generic; + +public class MyCollectionOfInt : IEnumerable { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + unsafe public MyCollectionOfInt(void* dummy = null){} + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(int l) => Array.Add(l); +} +"""; - .method public hidebysig specialname - instance int32 get_Item ( - " + typeString + @" a - ) cil managed - { - " + attributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret - } + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); - .method public hidebysig specialname - instance void set_Item ( - " + typeString + @" a, - int32 'value' - ) cil managed + string source2 = """ +class Program +{ + unsafe public static void Test(params MyCollectionOfInt a) { - " + attributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret } +} +"""; - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugDll); + comp2.VerifyEmitDiagnostics(); - .property instance int32 Item( - " + typeString + @" a - ) + string source3 = """ +public class Params +{ + public static void Test(params MyCollectionOfInt a) { - .get instance int32 Params1::get_Item(" + typeString + @") - .set instance void Params1::set_Item(" + typeString + @", int32) + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } } } +"""; -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute + var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.DebugDll); + comp3.VerifyEmitDiagnostics(); + + string source4 = """ +class Program { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + static void Main() { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + Params.Test(); + Params.Test(1); + Params.Test(2, 3); } } -"; +"""; - var src = @" + var comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.ReleaseExe); + comp4.VerifyEmitDiagnostics( + // (5,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test()").WithLocation(5, 9), + // (6,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(1); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test(1)").WithLocation(6, 9), + // (7,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(2, 3); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Params.Test(2, 3)").WithLocation(7, 9) + ); + + string source5 = """ class Program { - static void Main() + static unsafe void Main() { - _ = new Params1()[1]; - new Params1()[1] = 0; + Params.Test(); + Params.Test(1); + Params.Test(2, 3); } } -"; - var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - - VerifyParams(test1, isParamArray: (parameterAttributes & ParamsAttributes.Array) != 0, isParamCollection: (parameterAttributes & ParamsAttributes.Collection) != 0); +"""; - CompileAndVerify(comp, expectedOutput: "Test1Test1").VerifyDiagnostics(); + var comp5 = CreateCompilation(source5, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.UnsafeReleaseExe); + CompileAndVerify( + comp5, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } - [Theory] - [CombinatorialData] - public void MetadataImport_15_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] + public void UnsafeContext_02_Add() { - switch (parameterType, parameterAttributes) - { - case (ParamsAttributes.Array, ParamsAttributes.Collection): - case (ParamsAttributes.Collection, ParamsAttributes.Array): - break; - default: - return; - } - - var attributesString = GetAttributesIL(parameterAttributes); - bool isArrayType = parameterType == ParamsAttributes.Array; - var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1"; - - // public class Params1 - // { - // public int this[System.Collections.Generic.IEnumerable or long[] a] - // { - // [parameterAttributes] get - // { System.Console.Write("Test1"); return 0; } - // [parameterAttributes] set - // { System.Console.Write("Test1"); } - // } - // } - string il = @" -.class public auto ansi beforefieldinit Params1 - extends [mscorlib]System.Object + string source1 = """ +using System.Collections; +using System.Collections.Generic; + +public class MyCollectionOfInt : IEnumerable { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) - - - .method public hidebysig specialname - instance int32 get_Item ( - " + typeString + @" a - ) cil managed - { - " + attributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret - } - - .method public hidebysig specialname - instance void set_Item ( - " + typeString + @" a, - int32 'value' - ) cil managed - { - " + attributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + unsafe public void Add(int l, void* dummy = null) => Array.Add(l); +} +"""; - .property instance int32 Item( - " + typeString + @" a - ) + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); + + string source2 = """ +class Program +{ + unsafe public static void Test(params MyCollectionOfInt a) { - .get instance int32 Params1::get_Item(" + typeString + @") - .set instance void Params1::set_Item(" + typeString + @", int32) } } +"""; -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute + var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugDll); + comp2.VerifyEmitDiagnostics(); + + string source3 = """ +public class Params { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + public static void Test(params MyCollectionOfInt a) { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } } } -"; +"""; - var src = @" + var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.DebugDll); + comp3.VerifyEmitDiagnostics(); + + string source4 = """ class Program { static void Main() { - _ = new Params1()[1]; - new Params1()[1] = 0; + Params.Test(); + Params.Test(1); + Params.Test(2, 3); } } -"; - var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); +"""; - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + var comp4 = CreateCompilation(source4, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.ReleaseExe); + comp4.VerifyEmitDiagnostics( + // (6,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(1); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "1").WithLocation(6, 21), + // (7,21): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(2, 3); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "2").WithLocation(7, 21), + // (7,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Params.Test(2, 3); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "3").WithLocation(7, 24) + ); - VerifyParams(test1, isParamArray: parameterAttributes == ParamsAttributes.Array, isParamCollection: parameterAttributes == ParamsAttributes.Collection); + string source5 = """ +class Program +{ + static unsafe void Main() + { + Params.Test(); + Params.Test(1); + Params.Test(2, 3); + } +} +"""; - if (isArrayType) - { - comp.VerifyDiagnostics( - // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' - // _ = new Params1()[1]; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(6, 27), - // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params long[]' - // new Params1()[1] = 0; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params long[]").WithLocation(7, 23) - ); - } - else - { - comp.VerifyDiagnostics( - // (6,27): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' - // _ = new Params1()[1]; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(6, 27), - // (7,23): error CS1503: Argument 1: cannot convert from 'int' to 'params System.Collections.Generic.IEnumerable' - // new Params1()[1] = 0; - Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "params System.Collections.Generic.IEnumerable").WithLocation(7, 23) - ); - } + var comp5 = CreateCompilation(source5, references: [comp1Ref, comp3.ToMetadataReference()], options: TestOptions.UnsafeReleaseExe); + CompileAndVerify( + comp5, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped, + expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } - [Fact(Skip = "Get rid of LocalRewriter.MakeCallWithNoExplicitArgument, it cannot handle params collections")] // PROTOTYPE(ParamsCollections): enable and test runtime behavior - public void UsingPatternWithParamsTest() + [Fact] + public void Cycle_01() { - var source = @" + var src = """ +using System.Collections; using System.Collections.Generic; -ref struct S1 +class MyCollection : IEnumerable { - public void Dispose(params IEnumerable args){ } + public List Array; + public MyCollection(params MyCollection p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection p) => Array.Add(l); } -class C2 +class Program { static void Main() { - using (S1 c = new S1()) - { - } - S1 c1b = new S1(); - using (c1b) { } + Test(); + Test(1); + Test(2, 3); } -}"; - CreateCompilation(source).VerifyEmitDiagnostics(); + + static void Test(params MyCollection a) + { + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (20,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 9), + // (21,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(1); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 9), + // (22,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 9) + ); } - [Fact(Skip = "Get rid of LocalRewriter.MakeCallWithNoExplicitArgument, it cannot handle params collections")] // PROTOTYPE(ParamsCollections): enable and test runtime behavior - public void UsingPatternWithParamsTest_Foreach() + [Fact] + public void Cycle_02() { - var source = @" + var src = """ +using System.Collections; using System.Collections.Generic; -ref struct S1 +class MyCollection : IEnumerable { - public void Dispose(params IEnumerable args){ } - public int Current => 0; - public bool MoveNext() => false; + public List Array; + public MyCollection(params MyCollection p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -class C2 +class Program { - public S1 GetEnumerator() => default; - static void Main() { - foreach (var i in new C2()) - { - } + Test(); + Test(1); + Test(2, 3); } -}"; - CreateCompilation(source).VerifyEmitDiagnostics(); - } - - [Fact] - public void ERR_ExplicitImplParams_01() - { - var source = @" -using System.Collections.Generic; -interface I1 -{ - void M1(params IEnumerable args); - void M2(IEnumerable args); - void M3(params IEnumerable args); -} -class C2 : I1 -{ - void I1.M1(IEnumerable args) {} - void I1.M2(params IEnumerable args) {} - void I1.M3(params IEnumerable args) {} + static void Test(params MyCollection a) + { + } } -"; - CreateCompilation(source).VerifyDiagnostics( - // (13,13): error CS0466: 'C2.I1.M2(params IEnumerable)' should not have a params parameter since 'I1.M2(IEnumerable)' does not - // void I1.M2(params IEnumerable args) {} - Diagnostic(ErrorCode.ERR_ExplicitImplParams, "M2").WithArguments("C2.I1.M2(params System.Collections.Generic.IEnumerable)", "I1.M2(System.Collections.Generic.IEnumerable)").WithLocation(13, 13) +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (20,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 9), + // (21,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(1); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 9), + // (22,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 9) ); } [Fact] - public void ERR_ExplicitImplParams_02() + public void Cycle_03() { - // public interface I1 - // { - // void M1([ParamArrayAttribute] IEnumerable args); - // void M2([ParamCollectionAttribute] int[] args); - // } - var il = @" -.class interface public auto ansi abstract beforefieldinit I1 + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable { - .method public hidebysig newslot abstract virtual - instance void M1 ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 args - ) cil managed + public List Array; + public MyCollection() { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) + Array = new List(); } - .method public hidebysig newslot abstract virtual - instance void M2 ( - int32[] args - ) cil managed - { - .param [1] - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection p) => Array.Add(l); } -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute +class Program { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + static void Main() { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + Test(); + Test(1); + Test(2, 3); } -} -"; - - var source = @" -using System.Collections.Generic; -class C1 : I1 -{ - void I1.M1(IEnumerable args) {} - void I1.M2(int[] args) {} + static void Test(params MyCollection a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } + } } +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); -class C2 : I1 -{ - void I1.M1(params IEnumerable args) {} - void I1.M2(params int[] args) {} -} -"; - CreateCompilationWithIL(source, il).VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_01_Constructor() + public void Cycle_04() { var src = """ +using System.Collections; using System.Collections.Generic; +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection(params MyCollection p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection p) => Array.Add(l); +} + class Program { - public Program(params IEnumerable x) + static void Main() + { + Test([]); + Test([1]); + Test([2, 3]); + } + + static void Test(params MyCollection a) { } } """; - VerifyAttributeEmbedding( - src, - "Program..ctor", - "Program..ctor(params System.Collections.Generic.IEnumerable x)", - // (5,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // public Program(params IEnumerable x) - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 20) - ); - } + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - private void VerifyAttributeEmbedding(string src, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) - { - VerifyAttributeEmbedding(src1: src, src2: null, memberName, memberDisplay, moduleDiagnostic); + comp.VerifyEmitDiagnostics( + // (20,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 14), + // (21,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([1]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[1]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 14), + // (21,15): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([1]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "1").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 15), + // (22,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([2, 3]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[2, 3]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 14), + // (22,15): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([2, 3]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "2").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 15), + // (22,18): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([2, 3]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "3").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 18) + ); } - private void VerifyAttributeEmbedding(string src1, string src2, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) + [Fact] + public void Cycle_05() { - IEnumerable references = src2 is null ? [] : [CreateCompilation(src2).ToMetadataReference()]; - - var comp = CreateCompilation(src1, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - verify(comp, attributeIsEmbedded: true); - - var comp1 = CreateCompilation(ParamCollectionAttributeSource, options: TestOptions.ReleaseDll); - var comp1Ref = comp1.ToMetadataReference(); - var comp2 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp2, attributeIsEmbedded: false); - Assert.Contains(comp1Ref, comp2.GetUsedAssemblyReferences()); + var src = """ +using System.Collections; +using System.Collections.Generic; - var comp3 = CreateCompilation(src1, references, options: TestOptions.ReleaseModule); - comp3.VerifyEmitDiagnostics(moduleDiagnostic); - Assert.NotEmpty(moduleDiagnostic); +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection(params MyCollection p) + { + Array = new List(); + } - var comp4 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseModule.WithMetadataImportOptions(MetadataImportOptions.All)); - verify(comp4, attributeIsEmbedded: false); - Assert.Contains(comp1Ref, comp4.GetUsedAssemblyReferences()); + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} - const string brokenParamCollectionAttributeSource = @" -namespace System.Runtime.CompilerServices +class Program { - public sealed class ParamCollectionAttribute : Attribute + static void Main() + { + Test([]); + Test([1]); + Test([2, 3]); + } + + static void Test(params MyCollection a) { - public ParamCollectionAttribute(int x) { } } } -"; +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - var comp5 = CreateCompilation(brokenParamCollectionAttributeSource, options: TestOptions.ReleaseDll); - var comp5Ref = comp5.ToMetadataReference(); - var comp6 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseDll); - comp6.VerifyEmitDiagnostics( - // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' - // public Program(params IEnumerable x) - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) + comp.VerifyEmitDiagnostics( + // (20,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(20, 14), + // (21,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([1]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[1]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(21, 14), + // (22,14): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection)'. + // Test([2, 3]); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "[2, 3]").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection)").WithLocation(22, 14) ); + } - var comp7 = CreateCompilation(src1, references: references.Concat([comp5Ref]), options: TestOptions.ReleaseModule); - comp7.VerifyEmitDiagnostics( - // (5,20): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.ParamCollectionAttribute..ctor' - // public Program(params IEnumerable x) - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, moduleDiagnostic[0].SquiggledText).WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute", ".ctor").WithLocation(moduleDiagnostic[0].LocationLine, moduleDiagnostic[0].LocationCharacter) - ); + [Fact] + public void Cycle_06() + { + var src = """ +using System.Collections; +using System.Collections.Generic; - void verify(CSharpCompilation comp, bool attributeIsEmbedded) - { - // We want to test attribute embedding - Assert.Equal(attributeIsEmbedded, comp.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor) is null); +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection() + { + Array = new List(); + } - CompileAndVerify( - comp, - symbolValidator: (m) => - { - string adjustedMemberName = memberName; - string adjustedMemberDisplay = memberDisplay; - if (comp.Options.OutputKind == OutputKind.NetModule && memberName.StartsWith("<>")) - { - adjustedMemberName = adjustedMemberName.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); - adjustedMemberDisplay = adjustedMemberDisplay.Replace("<>", "<" + comp.SourceAssembly.Name + ">"); - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection p) => Array.Add(l); +} - MethodSymbol member = m.GlobalNamespace.GetMember(adjustedMemberName); - AssertEx.Equal(adjustedMemberDisplay, member.ToTestDisplayString()); - VerifyParamsAndAttribute(member.Parameters[0], isParamCollection: true); +class Program +{ + static void Main() + { + Test([]); + Test([1]); + Test([2, 3]); + } - if (member.AssociatedSymbol is PropertySymbol prop) - { - VerifyParams(prop.Parameters[0], isParamCollection: true); - } + static void Test(params MyCollection a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - if (attributeIsEmbedded) - { - Assert.NotNull(m.GlobalNamespace.GetMember("System.Runtime.CompilerServices.ParamCollectionAttribute")); - } - else if (!m.GlobalNamespace.GetMembers("System").IsEmpty) - { - Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); - } - }, - verify: comp.Options.OutputKind != OutputKind.NetModule ? - Verification.Passes : - Verification.Fails with - { - PEVerifyMessage = "The module was expected to contain an assembly manifest.", - ILVerifyMessage = "The format of a DLL or executable being loaded is invalid" - } - ).VerifyDiagnostics(); - } + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_02_Delegate() + public void Cycle_07() { var src = """ +using System.Collections; using System.Collections.Generic; -delegate void Program(params IEnumerable x); +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection(params MyCollection> p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection> p) => Array.Add(l); +} + +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + } +} """; - VerifyAttributeEmbedding( - src, - "Program.Invoke", - "void Program.Invoke(params System.Collections.Generic.IEnumerable x)", - // (3,23): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // delegate void Program(params IEnumerable x); - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(3, 23) + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (20,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(20, 9), + // (21,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(1); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(21, 9), + // (22,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(22, 9) ); } [Fact] - public void EmbedAttribute_03_AnonymousDelegate_FromMember() + public void Cycle_08() { - var src1 = @" + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection() + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection> p) => Array.Add(l); +} + class Program { static void Main() { - var x1 = Params.Test1; + Test(); + Test(1); + Test(2, 3); + } - x1(1); + static void Test(params MyCollection a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } } } -"; - var src2 = @" -using System.Collections.Generic; +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); -public class Params -{ - static public void Test1(params IEnumerable a) { } -} -"; - VerifyAttributeEmbedding( - src1, - src2, - "<>f__AnonymousDelegate0.Invoke", - "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", - // (6,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // var x1 = Params.Test1; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Params.Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(6, 18) - ); + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_04_AnonymousDelegate_FromLambda() + public void Cycle_09() { - var src = @" + var src = """ +using System.Collections; using System.Collections.Generic; +class MyCollection : IEnumerable +{ + public List Array; + public MyCollection(params MyCollection> p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + class Program { static void Main() { - var x1 = (params IEnumerable a) => {}; + Test(); + Test(1); + Test(2, 3); + } - x1(1); + static void Test(params MyCollection a) + { } } -"; - VerifyAttributeEmbedding( - src, - "<>f__AnonymousDelegate0.Invoke", - "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", - // (8,19): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // var x1 = (params IEnumerable a) => {}; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable a").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(8, 19) +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (20,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(20, 9), + // (21,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(1); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(21, 9), + // (22,9): error CS9506: Creation of params collection 'MyCollection' results in an infinite chain of invocation of constructor 'MyCollection.MyCollection(params MyCollection>)'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection", "MyCollection.MyCollection(params MyCollection>)").WithLocation(22, 9) ); } [Fact] - public void EmbedAttribute_05_AnonymousDelegate_FromLambda_ExpressionTree() + public void Cycle_10() { - var src = @" + var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq.Expressions; -class Program +class MyCollection1 : IEnumerable { - static void Main() + public List Array; + public MyCollection1(params MyCollection2 p) { - Test((params IEnumerable a) => {}); + Array = new List(); } - - static void Test(Expression e){} + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"; - CreateCompilation(src).VerifyEmitDiagnostics( - // (9,9): error CS0411: The type arguments for method 'Program.Test(Expression)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // Test((params IEnumerable a) => {}); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Test").WithArguments("Program.Test(System.Linq.Expressions.Expression)").WithLocation(9, 9) - ); - } - [Fact] - public void EmbedAttribute_06_AnonymousDelegate_FromLocalFunction() - { - var src = """ -using System.Collections.Generic; +class MyCollection2 : IEnumerable +{ + public List Array; + public MyCollection2(params MyCollection1 p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection3 : IEnumerable +{ + public List Array; + public MyCollection3(params MyCollection2 p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} class Program { static void Main() { - var x1 = Test1; - - x1(1); + Test(); + Test(1); + Test(2, 3); + } - void Test1(params IEnumerable a) { } + static void Test(params MyCollection3 a) + { } } """; - VerifyAttributeEmbedding( - src, - "<>f__AnonymousDelegate0.Invoke", - "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", - // (7,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // var x1 = Test1; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(7, 18) + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics( + // (44,9): error CS9506: Creation of params collection 'MyCollection3' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. + // Test(); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test()").WithArguments("MyCollection3", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(44, 9), + // (45,9): error CS9506: Creation of params collection 'MyCollection3' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. + // Test(1); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(1)").WithArguments("MyCollection3", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(45, 9), + // (46,9): error CS9506: Creation of params collection 'MyCollection3' results in an infinite chain of invocation of constructor 'MyCollection2.MyCollection2(params MyCollection1)'. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, "Test(2, 3)").WithArguments("MyCollection3", "MyCollection2.MyCollection2(params MyCollection1)").WithLocation(46, 9) ); } [Fact] - public void EmbedAttribute_07_RegularMethod() + public void Cycle_11() { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection1 : IEnumerable { - void Test(params IEnumerable x) + public List Array; + public MyCollection1(params int[] p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection2 : IEnumerable +{ + public List Array; + public MyCollection2(params MyCollection1 p) { + Array = new List(); } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"""; - VerifyAttributeEmbedding( - src, - "Program.Test", - "void Program.Test(params System.Collections.Generic.IEnumerable x)", - // (5,15): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // void Test(params IEnumerable x) - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 15) - ); - } - [Fact] - public void EmbedAttribute_08_Operator() - { - var src = @" -using System.Collections.Generic; +class MyCollection3 : IEnumerable +{ + public List Array; + public MyCollection3(params MyCollection2 p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} class Program { - public static implicit operator Program(params List x) + static void Main() { - return null; + Test(); + Test(1); + Test(2, 3); } - public static Program operator +(Program x, params List y) + static void Test(params MyCollection3 a) { - return null; + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } } } -"; - CreateCompilation(src).VerifyEmitDiagnostics( - // (6,45): error CS1670: params is not valid in this context - // public static implicit operator Program(params List x) - Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(6, 45), - // (11,49): error CS1670: params is not valid in this context - // public static Program operator +(Program x, params List y) - Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(11, 49) - ); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_09_Property_get() + public void Cycle_12() { var src = """ +using System.Collections; using System.Collections.Generic; +class MyCollection1 : IEnumerable +{ + public List Array; + public MyCollection1() + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection2 : IEnumerable +{ + public List Array; + public MyCollection2(params MyCollection1 p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection3 : IEnumerable +{ + public List Array; + public MyCollection3(params MyCollection2 p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + class Program { - int this[params IEnumerable x] - => 0; + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection3 a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } + } } """; - VerifyAttributeEmbedding( - src, - "Program.get_Item", - "System.Int32 Program.this[params System.Collections.Generic.IEnumerable x].get", - // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // int this[params IEnumerable x] - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) - ); + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_10_Property_get() + public void Cycle_13() { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection : IEnumerable { - int this[params IEnumerable x] - { get => 0; set {} } + public List Array; + public MyCollection(params int[] p) + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"""; - VerifyAttributeEmbedding( - src, - "Program.get_Item", - "System.Int32 Program.this[params System.Collections.Generic.IEnumerable x].get", - // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // int this[params IEnumerable x] - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) - ); + +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else + { + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_11_Property_set() + public void InvalidParamsTypeInPartialMethod() { var src = """ -using System.Collections.Generic; - -class Program +partial class Program { - int this[params IEnumerable x] - {set{}} + partial void Test1(params int a); + + partial void Test1(params int a) + { + } + + partial void Test2(int a); + + partial void Test2(params int a) + { + } + + partial void Test3(params int a); + + partial void Test3(int a) + { + } } """; - VerifyAttributeEmbedding( - src, - "Program.set_Item", - "void Program.this[params System.Collections.Generic.IEnumerable x].set", - // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // int this[params IEnumerable x] - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + comp.VerifyDiagnostics( + // (3,24): error CS0225: The params parameter must have a valid collection type + // partial void Test1(params int a); + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(3, 24), + // (5,24): error CS0225: The params parameter must have a valid collection type + // partial void Test1(params int a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(5, 24), + // (11,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // partial void Test2(params int a) + Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test2").WithLocation(11, 18), + // (11,24): error CS0225: The params parameter must have a valid collection type + // partial void Test2(params int a) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(11, 24), + // (15,24): error CS0225: The params parameter must have a valid collection type + // partial void Test3(params int a); + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(15, 24), + // (17,18): error CS0758: Both partial method declarations must use a params parameter or neither may use a params parameter + // partial void Test3(int a) + Diagnostic(ErrorCode.ERR_PartialMethodParamsDifference, "Test3").WithLocation(17, 18) ); } [Fact] - public void EmbedAttribute_12_Property_set() + public void InvalidParamsType() { var src = """ -using System.Collections.Generic; - -class Program +partial class Program { - int this[params IEnumerable x] - {get => 0; set{}} + int this[params int a] => a; + + void Test() + { + var x = (params int a) => a; + local (0); + + int local(params int a) => a; + } } + +delegate void D(params int a); """; - VerifyAttributeEmbedding( - src, - "Program.set_Item", - "void Program.this[params System.Collections.Generic.IEnumerable x].set", - // (5,14): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // int this[params IEnumerable x] - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable x").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(5, 14) + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + comp.VerifyDiagnostics( + // (3,14): error CS0225: The params parameter must have a valid collection type + // int this[params int a] => a; + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(3, 14), + // (7,18): error CS0225: The params parameter must have a valid collection type + // var x = (params int a) => a; + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(7, 18), + // (10,19): error CS0225: The params parameter must have a valid collection type + // int local(params int a) => a; + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(10, 19), + // (14,17): error CS0225: The params parameter must have a valid collection type + // delegate void D(params int a); + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(14, 17) ); } [Fact] - public void ConsumeAcrossAssemblyBoundary_01_Method() + public void CollectionWithRequiredMember_01() { - var src1 = """ + var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq; -public class Params +public class MyCollection1 : IEnumerable { - public static void Test(params IEnumerable a) - { - var array = a.ToArray(); - if (array.Length == 0) - { - System.Console.WriteLine(array.Length); - } - else - { - System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); - } - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(long l) => throw null; + + public required int F; } -"""; - var src2 = """ + +public class MyCollection2 : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(long l) => throw null; +} + class Program { static void Main() { - Params.Test(); - Params.Test(1); - Params.Test(2, 3); + Test(); + Test(1); + Test(2, 3); } -} -"""; - var comp1 = CreateCompilation(src1); - - verify(image: true); - verify(image: false); - - void verify(bool image) - { - var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -0 -1: 1 ... 1 -2: 2 ... 3 -")).VerifyDiagnostics(); - } - } - [Theory] - [CombinatorialData] - public void ConsumeAcrossAssemblyBoundary_02_Property(bool hasSet) - { - var src1 = """ -using System.Collections.Generic; -using System.Linq; + static void Test(params MyCollection1 a) + { + } -public class Params -{ - public int this[char c, params IEnumerable a] + [System.Obsolete] + static void Test(MyCollection2 a) { - get - { - var array = a.ToArray(); - if (array.Length == 0) - { - System.Console.WriteLine(array.Length); - } - else - { - System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); - } + } - return 0; - } -""" + (hasSet ? """ - set {} -""" : -"") + - """ + static void Test2(MyCollection1 a) + { } -} -"""; - var src2 = """ -class Program -{ - static void Main() + + static void Test2() { - var p = new Params(); - _ = p['a']; - _ = p['b', 1]; - _ = p['c', 2, 3]; + MyCollection1 b = [1]; + Test([2, 3]); + Test2([2, 3]); } } -"""; - var comp1 = CreateCompilation(src1); - - verify(image: true); - verify(image: false); - void verify(bool image) - { - var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -0 -1: 1 ... 1 -2: 2 ... 3 -")).VerifyDiagnostics(); - } + comp.VerifyDiagnostics( + // (24,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // Test(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test()").WithArguments("MyCollection1.F").WithLocation(24, 9), + // (25,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // Test(1); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test(1)").WithArguments("MyCollection1.F").WithLocation(25, 9), + // (26,9): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // Test(2, 3); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Test(2, 3)").WithArguments("MyCollection1.F").WithLocation(26, 9), + + // PROTOTYPE(ParamsCollections): I am not sure if we really want an error to be reported for params parameter in this case. + // Need to confirm that. If we do, perhaps the error should have a wording tailored for 'params'. + // The current wording looks confusing. + + // (29,22): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // static void Test(params MyCollection1 a) + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "params MyCollection1 a").WithArguments("MyCollection1.F").WithLocation(29, 22), + + // (44,27): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // MyCollection1 b = [1]; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "[1]").WithArguments("MyCollection1.F").WithLocation(44, 27), + // (45,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Test(params MyCollection1)' and 'Program.Test(MyCollection2)' + // Test([2, 3]); + Diagnostic(ErrorCode.ERR_AmbigCall, "Test").WithArguments("Program.Test(params MyCollection1)", "Program.Test(MyCollection2)").WithLocation(45, 9), + // (46,15): error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor. + // Test2([2, 3]); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "[2, 3]").WithArguments("MyCollection1.F").WithLocation(46, 15) + ); } - [Theory] - [CombinatorialData] - public void ConsumeAcrossAssemblyBoundary_03_Property(bool hasGet) + [Fact] + public void CollectionWithRequiredMember_02() { - var src1 = """ + var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics.CodeAnalysis; -public class Params +public class MyCollection : IEnumerable { - public int this[char c, params IEnumerable a] - { - set - { - var array = a.ToArray(); - if (array.Length == 0) - { - System.Console.WriteLine(array.Length); - } - else - { - System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); - } - } -""" + (hasGet ? """ - get => 0; -""" : -"") + - """ - } + [SetsRequiredMembers] + public MyCollection(){} + + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(long l) => Array.Add(l); + + public required int F; } -"""; - var src2 = """ + class Program { static void Main() { - var p = new Params(); - p['a'] = 0; - p['b', 1] = 1; - p['c', 2, 3] = 2; + Test(); + Test(1); + Test(2, 3); } -} -"""; - var comp1 = CreateCompilation(src1); - verify(image: true); - verify(image: false); + static void Test(params MyCollection a) + { + } - void verify(bool image) - { - var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); + static void Test2() + { + Test([2, 3]); + } +} - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -0 -1: 1 ... 1 -2: 2 ... 3 -")).VerifyDiagnostics(); - } +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics(); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 6e898d45c57bc..0eaf71678fb69 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -8480,10 +8480,7 @@ public void ParamsArray_NotArray() CreateCompilation(source).VerifyDiagnostics( // (1,12): error CS0225: The params parameter must have a valid collection type // var lam = (params int x) => x; - Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(1, 12), - // (1,23): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. - // var lam = (params int x) => x; - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "x").WithArguments("1").WithLocation(1, 23) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(1, 12) ); } @@ -8496,10 +8493,7 @@ public void ParamsArray_Multidimensional() CreateCompilation(source).VerifyDiagnostics( // (1,12): error CS0225: The params parameter must have a valid collection type // var lam = (params int[,] xs) => xs.Length; - Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(1, 12), - // (1,26): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. - // var lam = (params int[,] xs) => xs.Length; - Diagnostic(ErrorCode.WRN_ParamsArrayInLambdaOnly, "xs").WithArguments("1").WithLocation(1, 26) + Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(1, 12) ); }