Skip to content

Commit

Permalink
Fix serialization of attribute data to account for named params argum…
Browse files Browse the repository at this point in the history
…ent (#22231)
  • Loading branch information
jcouv committed Sep 29, 2017
1 parent 124b423 commit 999e20e
Show file tree
Hide file tree
Showing 3 changed files with 394 additions and 23 deletions.
88 changes: 66 additions & 22 deletions src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,12 @@ private ImmutableArray<TypedConstant> GetRewrittenAttributeConstructorArguments(

if (parameter.IsParams && parameter.Type.IsSZArray() && i + 1 == parameterCount)
{
reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, argumentsCount, argsConsumedCount, this.Conversions);
sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount);
reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, constructorArgumentNamesOpt, argumentsCount,
argsConsumedCount, this.Conversions, out bool foundNamed);
if (!foundNamed)
{
sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount);
}
}
else if (argsConsumedCount < argumentsCount)
{
Expand Down Expand Up @@ -820,41 +824,48 @@ private TypedConstant GetDefaultValueArgument(ParameterSymbol parameter, Attribu
}
}

private static TypedConstant GetParamArrayArgument(ParameterSymbol parameter, ImmutableArray<TypedConstant> constructorArgsArray, int argumentsCount, int argsConsumedCount, Conversions conversions)
private static TypedConstant GetParamArrayArgument(ParameterSymbol parameter, ImmutableArray<TypedConstant> constructorArgsArray,
ImmutableArray<string> constructorArgumentNamesOpt, int argumentsCount, int argsConsumedCount, Conversions conversions, out bool foundNamed)
{
Debug.Assert(argsConsumedCount <= argumentsCount);

// If there's a named argument, we'll use that
if (!constructorArgumentNamesOpt.IsDefault)
{
int argIndex = constructorArgumentNamesOpt.IndexOf(parameter.Name);
if (argIndex >= 0)
{
foundNamed = true;
if (TryGetNormalParamValue(parameter, constructorArgsArray, argIndex, conversions, out var namedValue))
{
return namedValue;
}

// A named argument for a params parameter is necessarily the only one for that parameter
return new TypedConstant(parameter.Type, ImmutableArray.Create(constructorArgsArray[argIndex]));
}
}

int paramArrayArgCount = argumentsCount - argsConsumedCount;
foundNamed = false;

// If there are zero arguments left
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)
// If there's exactly one argument left, we'll try to use it in normal form
if (paramArrayArgCount == 1 &&
TryGetNormalParamValue(parameter, constructorArgsArray, argsConsumedCount, conversions, out var lastValue))
{
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.ClassifyBuiltInConversion(argumentType, parameter.Type, ref useSiteDiagnostics);

// 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];
}
return lastValue;
}

Debug.Assert(!constructorArgsArray.IsDefault);
Debug.Assert(argsConsumedCount <= constructorArgsArray.Length);

// Take the trailing arguments as an array for expanded form
var values = new TypedConstant[paramArrayArgCount];

for (int i = 0; i < paramArrayArgCount; i++)
Expand All @@ -865,6 +876,39 @@ private static TypedConstant GetParamArrayArgument(ParameterSymbol parameter, Im
return new TypedConstant(parameter.Type, values.AsImmutableOrNull());
}

private static bool TryGetNormalParamValue(ParameterSymbol parameter, ImmutableArray<TypedConstant> constructorArgsArray,
int argIndex, Conversions conversions, out TypedConstant result)
{
TypedConstant argument = constructorArgsArray[argIndex];
if (argument.Kind != TypedConstantKind.Array)
{
result = default;
return false;
}

TypeSymbol argumentType = (TypeSymbol)argument.Type;
// Easy out (i.e. don't bother classifying conversion).
if (argumentType == parameter.Type)
{
result = argument;
return true;
}

HashSet<DiagnosticInfo> useSiteDiagnostics = null; // ignoring, since already bound argument and parameter
Conversion conversion = conversions.ClassifyBuiltInConversion(argumentType, parameter.Type, ref useSiteDiagnostics);

// 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)
{
result = argument;
return true;
}

result = default;
return false;
}

#endregion

#region AttributeExpressionVisitor
Expand Down
Loading

0 comments on commit 999e20e

Please sign in to comment.