private static TypedConstant GetParamArrayArgument(ParameterSymbol parameter, ImmutableArray<TypedConstant> constructorArgsArray, int argumentsCount, int argsConsumedCount, Conversions conversions) { Debug.Assert(argsConsumedCount <= argumentsCount); int paramArrayArgCount = argumentsCount - argsConsumedCount; if (paramArrayArgCount == 0) { return new TypedConstant(parameter.Type, ImmutableArray<TypedConstant>.Empty); } // If there's exactly one argument and it's an array of an appropriate type, then just return it. if (paramArrayArgCount == 1 && constructorArgsArray[argsConsumedCount].Kind == TypedConstantKind.Array) { TypeSymbol argumentType = (TypeSymbol)constructorArgsArray[argsConsumedCount].Type; // Easy out (i.e. don't both classifying conversion). if (argumentType == parameter.Type) { return constructorArgsArray[argsConsumedCount]; } HashSet<DiagnosticInfo> useSiteDiagnostics = null; // ignoring, since already bound argument and parameter Conversion conversion = conversions.ClassifyConversion(argumentType, parameter.Type, ref useSiteDiagnostics, builtinOnly: true); // NOTE: Won't always succeed, even though we've performed overload resolution. // For example, passing int[] to params object[] actually treats the int[] as an element of the object[]. if (conversion.IsValid && conversion.Kind == ConversionKind.ImplicitReference) { return constructorArgsArray[argsConsumedCount]; } } Debug.Assert(!constructorArgsArray.IsDefault); Debug.Assert(argsConsumedCount <= constructorArgsArray.Length); var values = new TypedConstant[paramArrayArgCount]; for (int i = 0; i < paramArrayArgCount; i++) { values[i] = constructorArgsArray[argsConsumedCount++]; } return new TypedConstant(parameter.Type, values.AsImmutableOrNull()); }
/// <summary> /// Gets the rewritten attribute constructor arguments, i.e. the arguments /// are in the order of parameters, which may differ from the source /// if named constructor arguments are used. /// /// For example: /// void Foo(int x, int y, int z, int w = 3); /// /// Foo(0, z: 2, y: 1); /// /// Arguments returned: 0, 1, 2, 3 /// </summary> /// <returns>Rewritten attribute constructor arguments</returns> /// <remarks> /// CONSIDER: Can we share some code will call rewriting in the local rewriter? /// </remarks> private ImmutableArray<TypedConstant> GetRewrittenAttributeConstructorArguments( out ImmutableArray<int> constructorArgumentsSourceIndices, MethodSymbol attributeConstructor, ImmutableArray<TypedConstant> constructorArgsArray, ImmutableArray<string> constructorArgumentNamesOpt, AttributeSyntax syntax, DiagnosticBag diagnostics, ref bool hasErrors) { Debug.Assert((object)attributeConstructor != null); Debug.Assert(!constructorArgsArray.IsDefault); Debug.Assert(!hasErrors); int argumentsCount = constructorArgsArray.Length; // argsConsumedCount keeps track of the number of constructor arguments // consumed from this.ConstructorArguments array int argsConsumedCount = 0; bool hasNamedCtorArguments = !constructorArgumentNamesOpt.IsDefault; Debug.Assert(!hasNamedCtorArguments || constructorArgumentNamesOpt.Length == argumentsCount); // index of the first named constructor argument int firstNamedArgIndex = -1; ImmutableArray<ParameterSymbol> parameters = attributeConstructor.Parameters; int parameterCount = parameters.Length; var reorderedArguments = new TypedConstant[parameterCount]; int[] sourceIndices = null; for (int i = 0; i < parameterCount; i++) { Debug.Assert(argsConsumedCount <= argumentsCount); ParameterSymbol parameter = parameters[i]; TypedConstant reorderedArgument; if (parameter.IsParams && parameter.Type.IsSZArray() && i + 1 == parameterCount) { reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, argumentsCount, argsConsumedCount, this.Conversions); sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); } else if (argsConsumedCount < argumentsCount) { if (!hasNamedCtorArguments || constructorArgumentNamesOpt[argsConsumedCount] == null) { // positional constructor argument reorderedArgument = constructorArgsArray[argsConsumedCount]; if (sourceIndices != null) { sourceIndices[i] = argsConsumedCount; } argsConsumedCount++; } else { // named constructor argument // Store the index of the first named constructor argument if (firstNamedArgIndex == -1) { firstNamedArgIndex = argsConsumedCount; } // Current parameter must either have a matching named argument or a default value // For the former case, argsConsumedCount must be incremented to note that we have // consumed a named argument. For the latter case, argsConsumedCount stays same. int matchingArgumentIndex; reorderedArgument = GetMatchingNamedOrOptionalConstructorArgument(out matchingArgumentIndex, constructorArgsArray, constructorArgumentNamesOpt, parameter, firstNamedArgIndex, argumentsCount, ref argsConsumedCount, syntax, diagnostics); sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); sourceIndices[i] = matchingArgumentIndex; } } else { reorderedArgument = GetDefaultValueArgument(parameter, syntax, diagnostics); sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); } if (!hasErrors) { if (reorderedArgument.Kind == TypedConstantKind.Error) { hasErrors = true; } else if (reorderedArgument.Kind == TypedConstantKind.Array && parameter.Type.TypeKind == TypeKind.Array && (TypeSymbol)reorderedArgument.Type != parameter.Type) { // NOTE: As in dev11, we don't allow array covariance conversions (presumably, we don't have a way to // represent the conversion in metadata). diagnostics.Add(ErrorCode.ERR_BadAttributeArgument, syntax.Location); hasErrors = true; } } reorderedArguments[i] = reorderedArgument; } constructorArgumentsSourceIndices = sourceIndices != null ? sourceIndices.AsImmutableOrNull() : default(ImmutableArray<int>); return reorderedArguments.AsImmutableOrNull(); }