From bb6aea4cd2713c15894a97fa28eb0810e494e3d1 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 1 Feb 2024 12:19:06 -0800 Subject: [PATCH 1/2] Align `params` type validation with the the spec https://github.com/dotnet/csharplang/blob/main/proposals/params-collections.md#method-parameters --- .../CSharp/Portable/Binder/Binder.cs | 2 + .../Portable/Binder/Binder_Attributes.cs | 2 +- .../Portable/Binder/Binder_Conversions.cs | 536 +- .../Portable/Binder/Binder_Expressions.cs | 222 +- .../Portable/Binder/Binder_Invocation.cs | 201 +- .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../ParamsCollectionTypeInProgressBinder.cs | 28 + .../Semantics/Conversions/Conversion.cs | 23 +- .../Semantics/Conversions/Conversions.cs | 9 +- .../OverloadResolution/OverloadResolution.cs | 51 +- .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../CSharp/Portable/Errors/ErrorFacts.cs | 2 + .../LocalRewriter_CollectionExpression.cs | 2 +- .../CSharp/Portable/Symbols/CompletionPart.cs | 4 +- .../Symbols/Source/ParameterHelpers.cs | 60 +- .../Source/SourceComplexParameterSymbol.cs | 193 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Semantics/CollectionExpressionTests.cs | 241 + .../Emit2/Semantics/ParamsCollectionTests.cs | 7241 +++++++++++------ .../Test/Semantic/Semantics/LambdaTests.cs | 10 +- 33 files changed, 6188 insertions(+), 2779 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Binder/ParamsCollectionTypeInProgressBinder.cs 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..34df5b10408f4 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,101 @@ 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 (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); + var d = BindingDiagnosticBag.GetInstance(); + bool toCheck = collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, boundElementInitializerExpressions[0].Type, addMethods: out _, d); + Debug.Assert(toCheck || result.HasErrors); + d.Free(); } +#endif + return result; - // 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, - 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) + 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 +6143,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 +6283,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 +6362,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 +6591,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..ec0b4fb0e1678 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1177,28 +1177,41 @@ 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: + { + binder.TryGetCollectionIterationType(CSharpSyntaxTree.Dummy.GetRoot(), type, out elementType); + + if (elementType.Type is null) + { + return false; + } + + if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable) + { + if (!binder.HasCollectionExpressionApplicableConstructor(CSharpSyntaxTree.Dummy.GetRoot(), type, constructor: out _, isExpanded: out _, BindingDiagnosticBag.Discarded)) + { + return false; + } + + if (!binder.HasCollectionExpressionApplicableAddMethod(CSharpSyntaxTree.Dummy.GetRoot(), type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) + { + return false; + } + } + } + break; } - elementType = default; - return false; + Debug.Assert(elementType.Type is { }); + return true; } /// @@ -2744,9 +2757,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..444215b38f14b 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,788 +1272,1896 @@ 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(); - - 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 -} -"); + 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) + ); } [Fact] - public void OrderOfEvaluation_03_ObjectInitializer() + public void ImplementsIEnumerableT_01_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() } }; + Test([2, 3]); } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - static void Test3() + // 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; + +class MyCollection : IEnumerable +{ + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(object l) => Array.Add(l); +} + +class Program +{ + static void Main() { - _ = new Program() { [GetA(), GetB(), GetC()] = { F1 = GetF1(), F2 = GetF2() } }; + Test(); + Test(1); + Test(2, 3); } - C1 this[int a, params MyCollection c] + static void Test(params MyCollection a) { - get + if (a.Array.Count == 0) { - System.Console.WriteLine("Get_this {0}", c is not null && (_c is null || (object)_c == c)); - _c = c; - return new C1(); + System.Console.WriteLine(a.Array.Count); } - set + else { - System.Console.WriteLine("Set_this {0}", (object)_c == c); + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); } } - - static int GetA() + static void Test2() { - System.Console.WriteLine("GetA"); - return 0; + Test([2, 3]); } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); - static int GetB() + 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(); + } + + [Fact] + public void ImplementsIEnumerable_02() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public IEnumerator GetEnumerator() => throw null; + public void Add(object l) => throw null; +} + +class Program +{ + static void Main() { - System.Console.WriteLine("GetB"); - return 0; + Test("2", 3); + Test(["2", 3]); } - static int GetC() + static void Test(params MyCollection a) { - System.Console.WriteLine("GetC"); - return 0; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + 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) + ); + } + + [Theory] + [CombinatorialData] + public void ImplementsIEnumerable_03_InAttribute(bool asStruct) + { + var src = @" +using System; +using System.Collections; + +" + (asStruct ? "struct" : "class") + @" MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw new InvalidOperationException(); + + public void Add(object l) {} +} + +[Test()] +class C1; + +[Test(1)] +class C2; + +[Test(2, 3)] +class C3; + +class Test : System.Attribute +{ + public Test(params MyCollection a) {} +} +"; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + 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) + ); + + 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.Object", typeInfo.ConvertedType.ToTestDisplayString()); + + Assert.True(model.GetConversion(expression).IsBoxing); + } + + 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("MyCollection", c1Arg.Type.ToTestDisplayString()); + Assert.Null(c1Arg.Value); + Assert.Throws(() => c1Arg.Values); + } + } + + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + [InlineData("ICollection")] + [InlineData("IList")] + public void ArrayInterfaces(string @interface) + { + var src = """ +using System.Collections.Generic; +using System.Linq; + +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + 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 Test2() + { + Test([2, 3]); + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + + 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(); + } + + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyCollection")] + [InlineData("IReadOnlyList")] + [InlineData("ICollection")] + [InlineData("IList")] + public void ArrayInterfaces_InAttribute(string @interface) + { + var src = @" +using System.Collections.Generic; + +[Test()] +class C1; + +[Test(1)] +class C2; + +[Test(2, 3)] +class C3; + +class Test : System.Attribute +{ + public Test(params " + @interface + @" 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 + // [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) + ); + + 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); + } + + 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 IEnumerable() + { + var src = """ +using System.Collections; + +class Program +{ + static void Test(params IEnumerable a) + { + Test(new object()); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); + + 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), + // (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) + ); + } + + [Fact] + public void WRN_ParamsArrayInLambdaOnly_01() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + System.Action> l = (params IEnumerable x) => {}; + l(null); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor); + + 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()); + + 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) + ); + + 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() + { + System.Action l = (params long[] x) => {}; + l(null); + } +} +"""; + + 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 = 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 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 +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + object 'object', + native int 'method' + ) runtime managed + { + } + + .method public hidebysig newslot virtual + instance void Invoke ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 args + ) runtime managed + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) + } +} + +.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 + { + } + + .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 + ) + } +} + +.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 src = """ +using System.Collections.Generic; + +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); + } +} +"""; + CreateCompilationWithIL(src, il).VerifyEmitDiagnostics(); + } + + [Fact] + public void WRN_ParamsArrayInLambdaOnly_03() + { + var src = """ +using System.Collections.Generic; + +public delegate void D1(params IEnumerable args); +public delegate void D2(params int[] args); + +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); + } +} +"""; + CreateCompilation(src).VerifyEmitDiagnostics(); + } + + [Fact] + public void ParamCollectionInLocalFunctionOnly() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + 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); + + 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()); + + Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + }).VerifyDiagnostics(); + + 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() + { + void local (params long[] x) {}; + local(1); + } +} +"""; + + comp = CreateCompilation(src2, 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 = "[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("", @" +{ + // 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 OrderOfEvaluation_01_NamedArguments() + { + 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(b: GetB(), c: GetC(), a: GetA()); + } + + static void Test(int a, int b, params MyCollection 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: @" +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] + public void OrderOfEvaluation_02_CompoundAssignment() + { + 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 +{ + 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() @@ -2861,7 +4084,7 @@ class C3 : C2 {} C2")).VerifyDiagnostics(); } - [Fact(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules] + [Fact] public void BetterNess_03_ElementType() { var src = @" @@ -2894,12 +4117,9 @@ public static void M2(params ushort[] x) 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) + // (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) ); } @@ -3602,7 +4822,7 @@ static void Main() CompileAndVerify(source, expectedOutput: "string[]"); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules + [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 @@ -3631,12 +4851,14 @@ static void Main() source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($$""" - F1({{spanType}}) - F2({{spanType}}) - F1({{spanType}}) - F2({{spanType}}) - """)); + 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) + ); } [Theory] @@ -3668,15 +4890,23 @@ static void Main() 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)); + // (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); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules + [Theory] [InlineData("System.ReadOnlySpan")] [InlineData("System.Span")] [InlineData("System.ReadOnlySpan")] @@ -3707,30 +4937,12 @@ static void Main() // 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) + // (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) ); } @@ -3761,15 +4973,23 @@ static void Main() 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)); + // (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); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules + [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 @@ -3804,13 +5024,17 @@ static void Main() source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: ExpectedOutput($$""" - F1({{spanType}}) - F2({{spanType}}) - """)); + 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) + ); } - [Theory(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules + [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 @@ -3845,36 +5069,17 @@ static void Main() targetFramework: TargetFramework.Net80, 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) + // (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(Skip = "Yes")] // PROTOTYPE(ParamsCollections): adjust for updated collection expression conversion rules + [Fact] public void BetterConversionFromExpression_String_05() // This is a clone of a unit-test from CollectionExpressionTests.cs { string source = $$""" @@ -3896,15 +5101,10 @@ static void Main() """; 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)); + // (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] @@ -5827,298 +7027,663 @@ public Test2(int a, params int[] b) [Fact] public void DynamicInvocation_ConstructorInitializer_02_AmbiguousDynamicParamsArgument() { - var src = """ -using System.Collections.Generic; + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + new C(d); + } + + class C(dynamic d) : Test(d); + + class Test + { + public Test(params IEnumerable b) + { + System.Console.Write("Called"); + } + } +} +"""; + 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) + ); + } + + [Fact] + public void DynamicInvocation_ConstructorInitializer_03_MultipleCandidates() + { + var src = """ +using System.Collections.Generic; + +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); + + 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 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 C07(dynamic d3) : Test3(d3, 1, 2); + class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 + + 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 + { + 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); + + 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_ConstructorInitializer_04() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + new C(d); + } + + class C(dynamic d) : 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) + { + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called True").VerifyDiagnostics(); + } + + [Fact] + public void DynamicInvocation_ConstructorInitializer_05() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1; + new C(d); + } + + class C(dynamic d) : Test(d, 2, 3); + + class Test + { + public Test(params IEnumerable b) + { + System.Console.Write("Called"); + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, options: TestOptions.ReleaseExe); + + CompileAndVerify( + comp, + expectedOutput: @"Called").VerifyDiagnostics(); + } + + [Fact] + public void DynamicInvocation_ConstructorInitializer_14_DoNotFilterBasedOnBetterFunctionMember() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() + { + dynamic d = 1L; + new C(d); + } +} + +class C(dynamic d) : Test(1, d, 2); + +class Test +{ + 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( + // (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 ExpressionTree() + { + var src = @" +using System.Linq.Expressions; class Program { static void Main() { - dynamic d = 1; - new C(d); + Expression e1 = () => Test(); + Expression e2 = () => Test(1); + Expression e3 = () => Test(2, 3); + Expression e4 = () => Test([]); } - class C(dynamic d) : Test(d); - - class Test + static void Test(params System.Collections.Generic.IEnumerable a) { - public Test(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( - // (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) + // (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 DynamicInvocation_ConstructorInitializer_03_MultipleCandidates() + public void MetadataImport_01_Method() { - var src = """ -using System.Collections.Generic; - -class Program + // 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 { - static void Main() + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed { - 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); + .param [1] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + 01 00 00 00 + ) - dynamic d3 = (byte)1; - new C07(d3); - new C08(d3, x); + .maxstack 8 - dynamic d4 = x; - new C09(d3, x); - new C10(d3, x); - new C11(d3, d4); + IL_0000: ldstr ""Test1"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - 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); + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) - class C07(dynamic d3) : Test3(d3, 1, 2); - class C08(int x, dynamic d3) : Test3(d3, x, x); // Called6 + .maxstack 8 - 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); + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } - class Test1 + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - public Test1(params IEnumerable b) => System.Console.Write("Called1"); - public Test1(System.DateTime b) => System.Console.Write("Called2"); + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } +} - class Test2 +.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 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"); + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Attribute::.ctor() + IL_0006: ret } +} +"; - class Test3 + 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() { - public Test3(byte x, params IEnumerable b) => System.Console.Write("Called6"); - public Test3(byte x, byte y, byte z) => System.Console.Write("Called7"); + Params.Test1(1); + Params.Test2(2); } +} +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + } - class Test4 + [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 { - public Test4(byte x, params IEnumerable b) => System.Console.Write("Called8"); - public Test4(byte x, long y, long z) => System.Console.Write("Called9"); + .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 } -} -"""; - var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp, 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) - ); - } + .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 + ) - [Fact] - public void DynamicInvocation_ConstructorInitializer_04() - { - var src = """ -using System.Collections.Generic; + .maxstack 8 -class Program -{ - static void Main() - { - dynamic d = 1; - new C(d); + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret } - class C(dynamic d) : Test(d, 2); - - class Test + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed { - public Test(int a, params IEnumerable b) - { - System.Console.Write("Called {0}", b is not null); - } + .maxstack 8 - public Test(int a, System.DateTime b) - { - } + 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: @"Called True").VerifyDiagnostics(); - } - [Fact] - public void DynamicInvocation_ConstructorInitializer_05() - { - 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, 2, 3); + .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.StandardAndCSharp, options: TestOptions.ReleaseExe); +"; - CompileAndVerify( - comp, - expectedOutput: @"Called").VerifyDiagnostics(); - } + var comp = CreateCompilationWithIL("", il); - [Fact] - public void DynamicInvocation_ConstructorInitializer_14_DoNotFilterBasedOnBetterFunctionMember() - { - 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 = 1L; - new C(d); + Params.Test1(1); + Params.Test2(2); } } +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + } -class C(dynamic d) : Test(1, d, 2); - -class Test + [Fact] + public void MetadataImport_03_Method() + { + // 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 { - public Test(long a1, params IEnumerable a2) + .method public hidebysig static + void Test1 ( + class [mscorlib]System.Collections.Generic.IEnumerable`1 a + ) cil managed { - System.Console.Write("long"); + .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 } - public Test(int b1, int b2, int b3) + .method public hidebysig static + void Test2 ( + int64[] a + ) cil managed { - System.Console.Write("int"); - } -} -"""; - 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 + ) - 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) - ); - } + .maxstack 8 - [Fact] - public void ExpressionTree() - { - var src = @" -using System.Linq.Expressions; + 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 { - Expression e1 = () => Test(); - Expression e2 = () => Test(1); - Expression e3 = () => Test(2, 3); - Expression e4 = () => Test([]); + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret } +} - static void Test(params System.Collections.Generic.IEnumerable a) +.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 = 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) - ); + 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); + + 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() + { + Params.Test1(1); + Params.Test2(2); + } +} +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); } [Fact] - public void MetadataImport_01_Method() + public void MetadataImport_04_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"); } + // 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 @@ -6130,7 +7695,7 @@ 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 ) @@ -6147,7 +7712,7 @@ int64[] a ) cil managed { .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) @@ -6189,17 +7754,17 @@ .maxstack 8 var test1 = comp.GetMember("Params.Test1").Parameters.Last(); var test2 = comp.GetMember("Params.Test2").Parameters.Last(); - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); comp = CreateCompilationWithIL("", il); @@ -6209,11 +7774,11 @@ .maxstack 8 Assert.Empty(test1.GetAttributes()); Assert.Empty(test2.GetAttributes()); - VerifyParamsAndAttribute(test1, isParamCollection: true); - VerifyParamsAndAttribute(test2, isParamArray: true); + VerifyParamsAndAttribute(test1, isParamArray: true); + VerifyParamsAndAttribute(test2, isParamCollection: true); - AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); - AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.ParamArrayAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString()); + AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString()); var src = @" class Program @@ -6226,23 +7791,45 @@ static void Main() } "; comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + 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) + ); } [Fact] - public void MetadataImport_02_Method() + public void MetadataImport_05_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[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 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 { @@ -6250,19 +7837,186 @@ .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: ldc.i4.0 + IL_000b: 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 + ) + { + .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 ) + .maxstack 8 + + IL_0000: ldstr ""Test2"" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: 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 + ) + { + .get instance int32 Params2::get_Item(int64[]) + } + +} + +.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("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 Params2()[2]; + } +} +"; + comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "Test1Test2").VerifyDiagnostics(); + } + + [Fact] + public void MetadataImport_06_Property() + { + // public class Params1 + // { + // 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 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 ( + 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 + 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 { @@ -6273,12 +8027,12 @@ 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 @@ -6290,6 +8044,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 @@ -6309,43 +8071,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, 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, 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 +8092,34 @@ static void Main() } [Fact] - public void MetadataImport_03_Method() + public void MetadataImport_07_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, 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 { @@ -6377,16 +8130,41 @@ 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 { @@ -6397,12 +8175,12 @@ 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 +8192,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,43 +8219,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, 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, 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]; } } "; @@ -6478,19 +8240,34 @@ static void Main() } [Fact] - public void MetadataImport_04_Method() + public void MetadataImport_08_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[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 { @@ -6498,16 +8275,41 @@ .param [1] .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 { @@ -6515,12 +8317,12 @@ .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 + IL_000a: ldc.i4.0 + IL_000b: ret } .method public hidebysig specialname rtspecialname @@ -6532,6 +8334,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 @@ -6545,79 +8355,56 @@ .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); - 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); - - test1 = comp.GetMember("Params.Test1").Parameters.Last(); - test2 = comp.GetMember("Params.Test2").Parameters.Last(); + } +} +"; - 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, 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); 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) + // (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_05_Property() + public void MetadataImport_09_Property() { // public class Params1 // { // public int this[params System.Collections.Generic.IEnumerable a] // { - // get - // { System.Console.Write("Test1"); return 0; } + // set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[params long[] a] // { - // get - // { System.Console.Write("Test2"); return 0; } + // set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6629,8 +8416,9 @@ 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] @@ -6641,8 +8429,7 @@ .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 +8446,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,8 +8458,9 @@ 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] @@ -6683,8 +8471,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 +8488,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 { @@ -6734,8 +8520,8 @@ class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; @@ -6744,22 +8530,22 @@ static void Main() } [Fact] - public void MetadataImport_06_Property() + public void MetadataImport_10_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamCollectionAttribute, ParamArrayAttribute] get - // { System.Console.Write("Test1"); return 0; } + // [ParamCollectionAttribute, ParamArrayAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamCollectionAttribute, ParamArrayAttribute] get - // { System.Console.Write("Test2"); return 0; } + // [ParamCollectionAttribute, ParamArrayAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6771,8 +8557,9 @@ 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] @@ -6786,8 +8573,7 @@ .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 +8590,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,8 +8602,9 @@ 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] @@ -6831,8 +8618,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 @@ -6849,11 +8635,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 +8667,8 @@ class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; @@ -6892,22 +8677,22 @@ static void Main() } [Fact] - public void MetadataImport_07_Property() + public void MetadataImport_11_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamArrayAttribute, ParamCollectionAttribute] get - // { System.Console.Write("Test1"); return 0; } + // [ParamArrayAttribute, ParamCollectionAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamArrayAttribute, ParamCollectionAttribute] get - // { System.Console.Write("Test2"); return 0; } + // [ParamArrayAttribute, ParamCollectionAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -6919,8 +8704,9 @@ 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] @@ -6934,8 +8720,7 @@ .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 +8737,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,8 +8749,9 @@ 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] @@ -6979,8 +8765,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 +8782,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 { @@ -7030,8 +8814,8 @@ class Program { static void Main() { - _ = new Params1()[1]; - _ = new Params2()[2]; + new Params1()[1] = 0; + new Params2()[2] = 0; } } "; @@ -7040,22 +8824,22 @@ static void Main() } [Fact] - public void MetadataImport_08_Property() + public void MetadataImport_12_Property() { // public class Params1 // { // public int this[System.Collections.Generic.IEnumerable a] // { - // [ParamArrayAttribute] get - // { System.Console.Write("Test1"); return 0; } + // [ParamArrayAttribute] set + // { System.Console.Write("Test1"); } // } // } // public class Params2 // { // public int this[long[] a] // { - // [ParamCollectionAttribute] get - // { System.Console.Write("Test2"); return 0; } + // [ParamCollectionAttribute] set + // { System.Console.Write("Test2"); } // } // } string il = @" @@ -7067,8 +8851,9 @@ 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] @@ -7079,8 +8864,7 @@ .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 @@ -7097,7 +8881,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) } } @@ -7109,8 +8893,9 @@ 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] @@ -7121,8 +8906,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 @@ -7139,11 +8923,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 { @@ -7172,38 +8955,91 @@ 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); 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) + // (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_09_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[params System.Collections.Generic.IEnumerable a] + // public int this[System.Collections.Generic.IEnumerable a] // { - // set + // [getAttributes] get + // { System.Console.Write("Test1"); return 0; } + // [setAttributes] set // { System.Console.Write("Test1"); } // } // } // public class Params2 // { - // public int this[params long[] a] + // public int this[long[] a] // { - // set + // [getAttributes] get + // { System.Console.Write("Test2"); return 0; } + // [setAttributes] set // { System.Console.Write("Test2"); } // } // } @@ -7215,16 +9051,28 @@ 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 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 - ) + " + setAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test1"" @@ -7246,6 +9094,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) } } @@ -7257,16 +9106,28 @@ 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 + { + " + getAttributesString + @" + .maxstack 8 + + IL_0000: ldstr ""Test2"" + 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, int32 'value' ) cil managed { - .param [1] - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) + " + setAttributesString + @" .maxstack 8 IL_0000: ldstr ""Test2"" @@ -7288,6 +9149,7 @@ .property instance int32 Item( int64[] a ) { + .get instance int32 Params2::get_Item(int64[]) .set instance void Params2::set_Item(int64[], int32) } @@ -7307,47 +9169,70 @@ .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 Params2()[2]; 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(); + 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,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) + ); } - [Fact] - public void MetadataImport_10_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[System.Collections.Generic.IEnumerable a] + // public int this[System.Collections.Generic.IEnumerable or long[] a] // { - // [ParamCollectionAttribute, ParamArrayAttribute] set + // [parameterAttributes] get + // { System.Console.Write("Test1"); return 0; } + // [parameterAttributes] 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 @@ -7356,67 +9241,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 - ) - .custom instance void [mscorlib]System.ParamArrayAttribute::.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 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 +9281,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,47 +9304,52 @@ .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) != 0, isParamCollection: (parameterAttributes & ParamsAttributes.Collection) != 0); + + CompileAndVerify(comp, expectedOutput: "Test1Test1").VerifyDiagnostics(); } - [Fact] - public void MetadataImport_11_Property() + [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; + } + + 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 a] + // public int this[System.Collections.Generic.IEnumerable or long[] a] // { - // [ParamArrayAttribute, ParamCollectionAttribute] set + // [parameterAttributes] get + // { System.Console.Write("Test1"); return 0; } + // [parameterAttributes] 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 @@ -7503,67 +9358,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 [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) - .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 - ) - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.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 } @@ -7579,13 +9398,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 { @@ -7601,132 +9421,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_12_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] 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 + 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 - ) - .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 System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( + .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 } - .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 { @@ -7742,1243 +9588,1604 @@ .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); - VerifyParams(test2, 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; } } -"; - 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..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) ); } - [Flags] - public enum ParamsAttributes + private void VerifyAttributeEmbedding(string src, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) { - None = 0, - Array = 1, - Collection = 2, - Both = Array | Collection, + VerifyAttributeEmbedding(src1: src, src2: null, memberName, memberDisplay, moduleDiagnostic); } - private string GetAttributesIL(ParamsAttributes attributes) + private void VerifyAttributeEmbedding(string src1, string src2, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) { - if (attributes == ParamsAttributes.None) - { - return ""; - } + IEnumerable references = src2 is null ? [] : [CreateCompilation(src2).ToMetadataReference()]; - string result = @" .param [1] -"; + var comp = CreateCompilation(src1, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); + verify(comp, attributeIsEmbedded: true); - if ((attributes & ParamsAttributes.Array) != 0) - { - result += @" - .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( - 01 00 00 00 - ) -"; - } + 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()); - if ((attributes & ParamsAttributes.Collection) != 0) - { - result += @" - .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( - 01 00 00 00 - ) -"; - } + var comp3 = CreateCompilation(src1, references, options: TestOptions.ReleaseModule); + comp3.VerifyEmitDiagnostics(moduleDiagnostic); + Assert.NotEmpty(moduleDiagnostic); - return result; - } + var comp4 = CreateCompilation(src1, references: references.Concat([comp1Ref]), options: TestOptions.ReleaseModule.WithMetadataImportOptions(MetadataImportOptions.All)); + verify(comp4, attributeIsEmbedded: false); + Assert.Contains(comp1Ref, comp4.GetUsedAssemblyReferences()); - [Theory] - [CombinatorialData] - public void MetadataImport_13_Property(ParamsAttributes getAttributes, ParamsAttributes setAttributes) - { - if (getAttributes == setAttributes) - { - return; - } + const string brokenParamCollectionAttributeSource = @" +namespace System.Runtime.CompilerServices +{ + public sealed class ParamCollectionAttribute : Attribute + { + public ParamCollectionAttribute(int x) { } + } +} +"; - var getAttributesString = GetAttributesIL(getAttributes); - var setAttributesString = GetAttributesIL(setAttributes); + 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) + ); - // 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 -{ - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + 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); - .method public hidebysig specialname - instance int32 get_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) cil managed - { - " + getAttributesString + @" - .maxstack 8 + 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 + ">"); + } - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: ret - } + MethodSymbol member = m.GlobalNamespace.GetMember(adjustedMemberName); + AssertEx.Equal(adjustedMemberDisplay, member.ToTestDisplayString()); + VerifyParamsAndAttribute(member.Parameters[0], isParamCollection: true); - .method public hidebysig specialname - instance void set_Item ( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a, - int32 'value' - ) cil managed - { - " + setAttributesString + @" - .maxstack 8 + if (member.AssociatedSymbol is PropertySymbol prop) + { + VerifyParams(prop.Parameters[0], isParamCollection: true); + } - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } + 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(); + } + } - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + [Fact] + public void EmbedAttribute_02_Delegate() + { + var src = """ +using System.Collections.Generic; - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } +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) + ); + } - .property instance int32 Item( - class [mscorlib]System.Collections.Generic.IEnumerable`1 a - ) + [Fact] + public void EmbedAttribute_03_AnonymousDelegate_FromMember() + { + var src1 = @" +class Program +{ + static void Main() { - .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) + var x1 = Params.Test1; + + x1(1); } } +"; + var src2 = @" +using System.Collections.Generic; -.class public auto ansi beforefieldinit Params2 - 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 - ) + 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; - .method public hidebysig specialname - instance int32 get_Item ( - int64[] a - ) cil managed +class Program +{ + static void Main() { - " + getAttributesString + @" - .maxstack 8 + var x1 = (params IEnumerable a) => {}; - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ldc.i4.0 - IL_000b: 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) + ); + } - .method public hidebysig specialname - instance void set_Item ( - int64[] a, - int32 'value' - ) cil managed - { - " + setAttributesString + @" - .maxstack 8 + [Fact] + public void EmbedAttribute_05_AnonymousDelegate_FromLambda_ExpressionTree() + { + var src = @" +using System.Collections.Generic; +using System.Linq.Expressions; - IL_0000: ldstr ""Test2"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret +class Program +{ + static void Main() + { + 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) + ); + } - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + [Fact] + public void EmbedAttribute_06_AnonymousDelegate_FromLocalFunction() + { + var src = """ +using System.Collections.Generic; + +class Program +{ + static void Main() { - .maxstack 8 + var x1 = Test1; - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + x1(1); - .property instance int32 Item( - int64[] a - ) - { - .get instance int32 Params2::get_Item(int64[]) - .set instance void Params2::set_Item(int64[], int32) + void Test1(params IEnumerable a) { } } - } -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute +"""; + 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) + ); + } + + [Fact] + public void EmbedAttribute_07_RegularMethod() + { + var src = """ +using System.Collections.Generic; + +class Program { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + void Test(params IEnumerable x) { - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret } } -"; +"""; + 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 { - static void Main() + public static implicit operator Program(params List x) { - _ = new Params1()[1]; - _ = new Params2()[2]; - new Params1()[1] = 0; - new Params2()[2] = 0; + return null; + } + + public static Program operator +(Program x, params List y) + { + return null; } } "; - var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); + 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 test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); - var test2 = comp.GetMember("Params2." + WellKnownMemberNames.Indexer).Parameters.Last(); + [Fact] + public void EmbedAttribute_09_Property_get() + { + var src = """ +using System.Collections.Generic; - VerifyParams(test1, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); - VerifyParams(test2, isParamArray: (setAttributes & ParamsAttributes.Array) != 0, isParamCollection: (setAttributes & ParamsAttributes.Collection) != 0); +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) + ); + } - string getModifier = getAttributes == ParamsAttributes.None ? "" : "params "; - string setModifier = setAttributes == ParamsAttributes.None ? "" : "params "; + [Fact] + public void EmbedAttribute_10_Property_get() + { + var src = """ +using System.Collections.Generic; - 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) +class Program +{ + int this[params IEnumerable x] + { get => 0; set {} } +} +"""; + 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) ); } - [Theory] - [CombinatorialData] - public void MetadataImport_14_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) + [Fact] + public void EmbedAttribute_11_Property_set() { - if (parameterAttributes == ParamsAttributes.None || - parameterType is not (ParamsAttributes.Array or ParamsAttributes.Collection) || - (parameterAttributes & parameterType) == 0) - { - return; - } + var src = """ +using System.Collections.Generic; - var attributesString = GetAttributesIL(parameterAttributes); - bool isArrayType = parameterType == ParamsAttributes.Array; - var typeString = isArrayType ? "int64[]" : "class [mscorlib]System.Collections.Generic.IEnumerable`1"; +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) + ); + } - // 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 + [Fact] + public void EmbedAttribute_12_Property_set() + { + var src = """ +using System.Collections.Generic; + +class Program { - .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( - 01 00 04 49 74 65 6d 00 00 - ) + 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; - .method public hidebysig specialname - instance int32 get_Item ( - " + typeString + @" a - ) cil managed +public class Params +{ + public static void Test(params IEnumerable a) { - " + 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 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]); + } } - - .method public hidebysig specialname - instance void set_Item ( - " + typeString + @" a, - int32 'value' - ) cil managed +} +"""; + var src2 = """ +class Program +{ + static void Main() { - " + attributesString + @" - .maxstack 8 - - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret + Params.Test(); + Params.Test(1); + Params.Test(2, 3); } +} +"""; + var comp1 = CreateCompilation(src1); - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + verify(image: true); + verify(image: false); - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + void verify(bool image) + { + var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - .property instance int32 Item( - " + typeString + @" a - ) - { - .get instance int32 Params1::get_Item(" + typeString + @") - .set instance void Params1::set_Item(" + typeString + @", int32) - } -} + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + } + } -.class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.ParamCollectionAttribute - extends [mscorlib]System.Attribute + [Theory] + [CombinatorialData] + public void ConsumeAcrossAssemblyBoundary_02_Property(bool hasSet) + { + var src1 = """ +using System.Collections.Generic; +using System.Linq; + +public class Params { - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed + public int this[char c, params IEnumerable a] { - .maxstack 8 + 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]); + } - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Attribute::.ctor() - IL_0006: ret + return 0; + } +""" + (hasSet ? """ + set {} +""" : +"") + + """ } } -"; - - var src = @" +"""; + var src2 = """ class Program { static void Main() { - _ = new Params1()[1]; - new Params1()[1] = 0; + var p = new Params(); + _ = p['a']; + _ = p['b', 1]; + _ = p['c', 2, 3]; } } -"; - var comp = CreateCompilationWithIL(src, il, options: TestOptions.ReleaseExe); +"""; + var comp1 = CreateCompilation(src1); - var test1 = comp.GetMember("Params1." + WellKnownMemberNames.Indexer).Parameters.Last(); + verify(image: true); + verify(image: false); - VerifyParams(test1, isParamArray: (parameterAttributes & ParamsAttributes.Array) != 0, isParamCollection: (parameterAttributes & ParamsAttributes.Collection) != 0); + void verify(bool image) + { + var comp2 = CreateCompilation(src2, references: [image ? comp1.EmitToImageReference() : comp1.ToMetadataReference()], options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "Test1Test1").VerifyDiagnostics(); + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + } } [Theory] [CombinatorialData] - public void MetadataImport_15_Property(ParamsAttributes parameterType, ParamsAttributes parameterAttributes) + public void ConsumeAcrossAssemblyBoundary_03_Property(bool hasGet) { - switch (parameterType, parameterAttributes) + var src1 = """ +using System.Collections.Generic; +using System.Linq; + +public class Params +{ + public int this[char c, params IEnumerable a] + { + set + { + var array = a.ToArray(); + if (array.Length == 0) { - case (ParamsAttributes.Array, ParamsAttributes.Collection): - case (ParamsAttributes.Collection, ParamsAttributes.Array): - break; - default: - return; + System.Console.WriteLine(array.Length); } - - 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 + else + { + System.Console.WriteLine("{0}: {1} ... {2}", array.Length, array[0], array[array.Length - 1]); + } + } +""" + (hasGet ? """ + get => 0; +""" : +"") + + """ + } +} +"""; + 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 ( - " + typeString + @" a - ) cil managed + static void Main() { - " + 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 p = new Params(); + p['a'] = 0; + p['b', 1] = 1; + p['c', 2, 3] = 2; } +} +"""; + 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); - .method public hidebysig specialname - instance void set_Item ( - " + typeString + @" a, - int32 'value' - ) cil managed - { - " + attributesString + @" - .maxstack 8 + CompileAndVerify( + comp2, + verify: image ? Verification.Passes : Verification.Skipped, + expectedOutput: ExpectedOutput(@" +0 +1: 1 ... 1 +2: 2 ... 3 +")).VerifyDiagnostics(); + } + } - IL_0000: ldstr ""Test1"" - IL_0005: call void [mscorlib]System.Console::Write(string) - IL_000a: ret - } + [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){} - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - .maxstack 8 + public List Array = new List(); + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public void Add(int l) => Array.Add(l); +} +"""; - IL_0000: ldarg.0 - IL_0001: call instance void [mscorlib]System.Object::.ctor() - IL_0006: ret - } + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); - .property instance int32 Item( - " + typeString + @" a - ) + 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( + // (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) + ); - 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] + [WorkItem("https://github.com/dotnet/roslyn/issues/71840")] + public void UnsafeContext_02_Add() { - var source = @" + string source1 = """ +using System.Collections; using System.Collections.Generic; - -ref struct S1 + +public class MyCollectionOfInt : IEnumerable { - public void Dispose(params IEnumerable args){ } + 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); } +"""; -class C2 + var comp1 = CreateCompilation(source1, options: TestOptions.UnsafeDebugDll); + var comp1Ref = comp1.EmitToImageReference(); + + string source2 = """ +class Program { - static void Main() + unsafe public static void Test(params MyCollectionOfInt a) { - using (S1 c = new S1()) - { - } - S1 c1b = new S1(); - using (c1b) { } } -}"; - CreateCompilation(source).VerifyEmitDiagnostics(); - } - - [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; - -ref struct S1 -{ - public void Dispose(params IEnumerable args){ } - public int Current => 0; - public bool MoveNext() => false; } +"""; -class C2 -{ - public S1 GetEnumerator() => default; + var comp2 = CreateCompilation(source2, references: [comp1Ref], options: TestOptions.UnsafeDebugDll); + comp2.VerifyEmitDiagnostics(); - static void Main() + string source3 = """ +public class Params +{ + public static void Test(params MyCollectionOfInt a) { - foreach (var i in new C2()) + 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(source).VerifyEmitDiagnostics(); - } +} +"""; - [Fact] - public void ERR_ExplicitImplParams_01() - { - var source = @" -using System.Collections.Generic; + var comp3 = CreateCompilation(source3, references: [comp1Ref], options: TestOptions.DebugDll); + comp3.VerifyEmitDiagnostics(); -interface I1 + string source4 = """ +class Program { - void M1(params IEnumerable args); - void M2(IEnumerable args); - void M3(params IEnumerable args); + static void Main() + { + Params.Test(); + Params.Test(1); + Params.Test(2, 3); + } } -class C2 : I1 +"""; + + 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) + ); + + string source5 = """ +class Program { - void I1.M1(IEnumerable args) {} - void I1.M2(params IEnumerable args) {} - void I1.M3(params IEnumerable args) {} + static unsafe void Main() + { + Params.Test(); + Params.Test(1); + Params.Test(2, 3); + } } -"; - 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 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] - public void ERR_ExplicitImplParams_02() + public void Cycle_01() { - // 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(params MyCollection p) { - .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 + 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); - var source = @" + 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 Cycle_02() + { + var src = """ +using System.Collections; using System.Collections.Generic; -class C1 : I1 +class MyCollection : IEnumerable { - void I1.M1(IEnumerable args) {} - void I1.M2(int[] args) {} + 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 : I1 +class Program { - void I1.M1(params IEnumerable args) {} - void I1.M2(params int[] args) {} + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + } } -"; - CreateCompilationWithIL(source, il).VerifyDiagnostics(); +"""; + 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_01_Constructor() + public void Cycle_03() { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection : IEnumerable { - public Program(params IEnumerable x) + public List Array; + public MyCollection() { + Array = new List(); } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l, params MyCollection p) => Array.Add(l); } -"""; - 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) +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + if (a.Array.Count == 0) { - VerifyAttributeEmbedding(src1: src, src2: null, memberName, memberDisplay, moduleDiagnostic); + System.Console.WriteLine(a.Array.Count); } - - private void VerifyAttributeEmbedding(string src1, string src2, string memberName, string memberDisplay, params DiagnosticDescription[] moduleDiagnostic) + else { - IEnumerable references = src2 is null ? [] : [CreateCompilation(src2).ToMetadataReference()]; + 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); - var comp = CreateCompilation(src1, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); - comp.MakeMemberMissing(WellKnownMember.System_ParamArrayAttribute__ctor); - verify(comp, attributeIsEmbedded: true); + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); + } - 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()); + [Fact] + public void Cycle_04() + { + 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, params MyCollection p) => Array.Add(l); +} - const string brokenParamCollectionAttributeSource = @" -namespace System.Runtime.CompilerServices +class Program { - public sealed class ParamCollectionAttribute : Attribute + static void Main() { - public ParamCollectionAttribute(int x) { } + Test([]); + Test([1]); + Test([2, 3]); } -} -"; - 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) - ); + static void Test(params MyCollection a) + { + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); - 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) + 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) ); - - 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 EmbedAttribute_02_Delegate() + public void Cycle_05() { var src = """ +using System.Collections; using System.Collections.Generic; -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) - ); - } +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); +} - [Fact] - public void EmbedAttribute_03_AnonymousDelegate_FromMember() - { - var src1 = @" class Program { static void Main() { - var x1 = Params.Test1; + Test([]); + Test([1]); + Test([2, 3]); + } - x1(1); + static void Test(params MyCollection a) + { } } -"; - 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) + 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) ); } [Fact] - public void EmbedAttribute_04_AnonymousDelegate_FromLambda() + public void Cycle_06() { - var src = @" + 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 IEnumerable a) => {}; + 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]); + } } } -"; - 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); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_05_AnonymousDelegate_FromLambda_ExpressionTree() + public void Cycle_07() { - var src = @" + var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq.Expressions; + +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((params IEnumerable a) => {}); + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection 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) +"""; + 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_06_AnonymousDelegate_FromLocalFunction() + public void Cycle_08() { 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 = Test1; - - x1(1); + Test(); + Test(1); + Test(2, 3); + } - void Test1(params IEnumerable a) { } + 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]); + } } } """; - 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); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_07_RegularMethod() + public void Cycle_09() { 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 { - void Test(params IEnumerable x) + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) { } } """; - 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) + 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_08_Operator() + public void Cycle_10() { - var src = @" + var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection1 : IEnumerable { - public static implicit operator Program(params List x) + public List Array; + public MyCollection1(params MyCollection2 p) { - return null; + Array = new List(); } - public static Program operator +(Program x, params List y) + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection2 : IEnumerable +{ + public List Array; + public MyCollection2(params MyCollection1 p) { - return null; + Array = new List(); } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"; - 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) - ); - } - [Fact] - public void EmbedAttribute_09_Property_get() - { - 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 { - int this[params IEnumerable x] - => 0; + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection3 a) + { + } } """; - 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); + + 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_10_Property_get() + public void Cycle_11() { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection1 : IEnumerable { - int this[params IEnumerable x] - { get => 0; set {} } + public List Array; + public MyCollection1(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) - ); - } - [Fact] - public void EmbedAttribute_11_Property_set() +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() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection3 a) + { + if (a.Array.Count == 0) + { + System.Console.WriteLine(a.Array.Count); + } + else { - var src = """ -using System.Collections.Generic; - -class Program -{ - int this[params IEnumerable x] - {set{}} + System.Console.WriteLine("{0}: {1} ... {2}", a.Array.Count, a.Array[0], a.Array[a.Array.Count - 1]); + } + } } """; - 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.ReleaseExe); + + CompileAndVerify(comp, expectedOutput: @" +0 +1: 1 ... 1 +2: 2 ... 3 +").VerifyDiagnostics(); } [Fact] - public void EmbedAttribute_12_Property_set() + public void Cycle_12() { var src = """ +using System.Collections; using System.Collections.Generic; -class Program +class MyCollection1 : IEnumerable { - int this[params IEnumerable x] - {get => 0; set{}} + public List Array; + public MyCollection1() + { + Array = new List(); + } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"""; - 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; +class MyCollection2 : IEnumerable +{ + public List Array; + public MyCollection2(params MyCollection1 p) + { + Array = new List(); + } -public class Params + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); +} + +class MyCollection3 : IEnumerable { - public static void Test(params IEnumerable a) + public List Array; + public MyCollection3(params MyCollection2 p) { - 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]); - } + Array = new List(); } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"""; - var src2 = """ + class Program { static void Main() { - Params.Test(); - Params.Test(1); - Params.Test(2, 3); + 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]); + } } } """; - 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, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" + CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 -")).VerifyDiagnostics(); - } +").VerifyDiagnostics(); } - [Theory] - [CombinatorialData] - public void ConsumeAcrossAssemblyBoundary_02_Property(bool hasSet) + [Fact] + public void Cycle_13() { - var src1 = """ + var src = """ +using System.Collections; using System.Collections.Generic; -using System.Linq; -public class Params +class MyCollection : IEnumerable { - public int this[char c, params IEnumerable a] + public List Array; + public MyCollection(params int[] p) { - 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 {} -""" : -"") + - """ + Array = new List(); } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object l) => Array.Add(l); } -"""; - var src2 = """ + class Program { static void Main() { - var p = new Params(); - _ = p['a']; - _ = p['b', 1]; - _ = p['c', 2, 3]; + 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 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, options: TestOptions.ReleaseExe); - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" + CompileAndVerify(comp, expectedOutput: @" 0 1: 1 ... 1 2: 2 ... 3 -")).VerifyDiagnostics(); - } +").VerifyDiagnostics(); } - [Theory] - [CombinatorialData] - public void ConsumeAcrossAssemblyBoundary_03_Property(bool hasGet) + [Fact] + public void InvalidParamsTypeInPartialMethod() { - var src1 = """ -using System.Collections.Generic; -using System.Linq; - -public class Params + var src = """ +partial class Program { - public int this[char c, params IEnumerable a] + 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) { - 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 src2 = """ -class Program + 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 InvalidParamsType() + { + var src = """ +partial class Program { - static void Main() + int this[params int a] => a; + + void Test() { - var p = new Params(); - p['a'] = 0; - p['b', 1] = 1; - p['c', 2, 3] = 2; + var x = (params int a) => a; + local (0); + + int local(params int a) => a; } } -"""; - 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); +delegate void D(params int a); +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseDll); - CompileAndVerify( - comp2, - verify: image ? Verification.Passes : Verification.Skipped, - expectedOutput: ExpectedOutput(@" -0 -1: 1 ... 1 -2: 2 ... 3 -")).VerifyDiagnostics(); - } + 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) + ); } } } 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) ); } From 9562c7c9baa1bc082c7e969a8cfd8eb04d26d4ee Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 6 Feb 2024 18:27:18 -0800 Subject: [PATCH 2/2] PR feedback --- .../Portable/Binder/Binder_Expressions.cs | 9 +- .../OverloadResolution/OverloadResolution.cs | 7 +- .../Source/SourceComplexParameterSymbol.cs | 28 +-- .../Emit2/Semantics/ParamsCollectionTests.cs | 182 +++++++++++++++++- 4 files changed, 204 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 34df5b10408f4..3a0801bdc202a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -5917,14 +5917,17 @@ private BoundExpression BindCollectionInitializerElementAddMethod( var result = bindCollectionInitializerElementAddMethod(elementInitializer, boundElementInitializerExpressions, collectionInitializerAddMethodBinder, diagnostics, implicitReceiver); #if DEBUG - if (boundElementInitializerExpressions.Length == 1 && + if (!result.HasErrors && + boundElementInitializerExpressions.Length == 1 && boundElementInitializerExpressions[0] is not ({ Type: null } or BoundLiteral or BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }) && !implicitReceiver.Type.IsDynamic()) { var d = BindingDiagnosticBag.GetInstance(); - bool toCheck = collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, boundElementInitializerExpressions[0].Type, addMethods: out _, d); - Debug.Assert(toCheck || result.HasErrors); + + // 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)); + d.Free(); } #endif diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index ec0b4fb0e1678..6bcac760fce74 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1187,7 +1187,8 @@ public static bool TryInferParamsCollectionIterationType(Binder binder, TypeSymb case CollectionExpressionTypeKind.ImplementsIEnumerable: case CollectionExpressionTypeKind.CollectionBuilder: { - binder.TryGetCollectionIterationType(CSharpSyntaxTree.Dummy.GetRoot(), type, out elementType); + SyntaxNode syntax = CSharpSyntaxTree.Dummy.GetRoot(); + binder.TryGetCollectionIterationType(syntax, type, out elementType); if (elementType.Type is null) { @@ -1196,12 +1197,12 @@ public static bool TryInferParamsCollectionIterationType(Binder binder, TypeSymb if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable) { - if (!binder.HasCollectionExpressionApplicableConstructor(CSharpSyntaxTree.Dummy.GetRoot(), type, constructor: out _, isExpanded: out _, BindingDiagnosticBag.Discarded)) + if (!binder.HasCollectionExpressionApplicableConstructor(syntax, type, constructor: out _, isExpanded: out _, BindingDiagnosticBag.Discarded)) { return false; } - if (!binder.HasCollectionExpressionApplicableAddMethod(CSharpSyntaxTree.Dummy.GetRoot(), type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 597acfbbe2603..bfbf2d5c659f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -1522,7 +1522,7 @@ private void ValidateParams() { if (state.NotePartComplete(CompletionPart.StartParamsValidation)) { - if (IsParams) + if (IsParams && ParameterSyntax?.Modifiers.Any(SyntaxKind.ParamsKeyword) == true) { var diagnostics = BindingDiagnosticBag.GetInstance(); validateParamsType(diagnostics); @@ -1639,24 +1639,24 @@ void validateParamsType(BindingDiagnosticBag diagnostics) { MessageID.IDS_ParamsCollections.CheckFeatureAvailability(diagnostics, ParameterSyntax); } + } - bool isAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics) - { - var useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics); + 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); + bool result = method.IsAsRestrictive(ContainingSymbol, ref useSiteInfo) && + method.ContainingType.IsAtLeastAsVisibleAs(ContainingSymbol, ref useSiteInfo); - diagnostics.Add(syntax.Location, useSiteInfo); - return result; - } + diagnostics.Add(syntax.Location, useSiteInfo); + return result; + } - void checkIsAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics) + void checkIsAtLeastAsVisible(ParameterSyntax syntax, Binder binder, MethodSymbol method, BindingDiagnosticBag diagnostics) + { + if (!isAtLeastAsVisible(syntax, binder, method, diagnostics)) { - if (!isAtLeastAsVisible(syntax, binder, method, diagnostics)) - { - diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, method, ContainingSymbol); - } + diagnostics.Add(ErrorCode.ERR_ParamsMemberCannotBeLessVisibleThanDeclaringMember, syntax, method, ContainingSymbol); } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 444215b38f14b..1e8cb9b2b1f13 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -2015,7 +2015,52 @@ static void Test(params MyCollection a) } [Fact] - public void ImplementsIEnumerableT_01_AddIsNotAnExtension() + public void ImplementsIEnumerableT_20_LessAccessibleConstructorAndAdd_NoError_In_LambdaOrLocalFunction() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +public class MyCollection : IEnumerable +{ + internal MyCollection() { } + internal void Add(string s) { } + + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +public class Program +{ + public void Test1() + { + local(); + local("a"); + + 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 ImplementsIEnumerableT_21_AddIsNotAnExtension() { var src = """ using System.Collections; @@ -4935,7 +4980,6 @@ static void Main() source, targetFramework: TargetFramework.Net80); - // Inline collection expression results in an ambiguity. 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)"); } @@ -11187,5 +11231,139 @@ void Test() Diagnostic(ErrorCode.ERR_ParamsMustBeCollection, "params").WithLocation(14, 17) ); } + + [Fact] + public void CollectionWithRequiredMember_01() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +public class MyCollection1 : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(long l) => throw null; + + public required int F; +} + +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() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection1 a) + { + } + + [System.Obsolete] + static void Test(MyCollection2 a) + { + } + + static void Test2(MyCollection1 a) + { + } + + static void Test2() + { + MyCollection1 b = [1]; + Test([2, 3]); + Test2([2, 3]); + } +} + +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + 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) + ); + } + + [Fact] + public void CollectionWithRequiredMember_02() + { + var src = """ +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +public class MyCollection : IEnumerable +{ + [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; +} + +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } + + static void Test(params MyCollection a) + { + } + + static void Test2() + { + Test([2, 3]); + } +} + +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + + comp.VerifyEmitDiagnostics(); + } } }