-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix serialization of attribute data to account for named params argument #22231
Conversation
@dotnet/roslyn-compiler for review. Thanks |
public object[] B { get; } | ||
} | ||
|
||
[Mark(b: new object[] { ""Hello"", ""World"" }, a: true)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider testing [Mark(b: "Hello", a: true)]
as well. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. That is broken. I'm investigating. #Resolved
public static void Main() | ||
{ | ||
Console.Write(""Method call: ""); | ||
Test(b: new object[] { ""Hello"", ""World"" }, a: true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this Test
necessary? #Resolved
[WorkItem(20741, "https://github.com/dotnet/roslyn/issues/20741")] | ||
public void TestNamedArgumentOnStringParamsArgument() | ||
{ | ||
var source = CreateCompilationWithMscorlib46(@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor point: Can use CreateStandardCompilation
in these tests. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed 46 for those reflection APIs.
} | ||
|
||
TypeSymbol argumentType = (TypeSymbol)argument.Type; | ||
// Easy out (i.e. don't both classifying conversion). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't bother
? #Resolved
|
||
// A named argument for a params parameter must be trailing, so expanded params must have a single value | ||
var singleValue = new TypedConstant[1] { constructorArgsArray[argIndex] }; | ||
return new TypedConstant(parameter.Type, singleValue.AsImmutableOrNull()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid the extra array with var singleValue = ImmutableArray.Create(constructorArgsArray[argIndex]));
#Resolved
if (!constructorArgumentNamesOpt.IsDefault) | ||
{ | ||
int argIndex = constructorArgumentNamesOpt.IndexOf(parameter.Name); | ||
if (TryGetNormalParamValue(parameter, constructorArgsArray, argIndex, conversions, out var namedValue)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does TryGetNormalParamValue
handle argIndex = -1
? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's indeed a problem there. Thanks
Fixed in new commit #Resolved
{ | ||
TypedConstant argument = constructorArgsArray[argIndex]; | ||
if (argument.Kind != TypedConstantKind.Array) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we hit this case with Mark(a: true, b: null)
? (We should generate a null
array.)
int paramArrayArgCount = argumentsCount - argsConsumedCount; | ||
|
||
// If there are zero arguments left | ||
if (paramArrayArgCount == 0) | ||
{ | ||
return new TypedConstant(parameter.Type, ImmutableArray<TypedConstant>.Empty); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor point: consider handling the zero arguments left
case first. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I think it must be after the named arguments case.
In reply to: 140549685 [](ancestors = 140549685)
return namedValue; | ||
} | ||
|
||
// A named argument for a params parameter must be trailing, so expanded params must have a single value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// A named argument for a params parameter must be trailing, so expanded params must have a single value [](start = 20, length = 104)
Named argument doesn't have to be trailing, so this comment is somewhat confusing. Consider rephrasing to something like: "Using an expanded form, the argument is the only element of the array." #Closed
@@ -578,7 +578,7 @@ private TypeSymbol BindNamedAttributeArgumentType(AttributeArgumentSyntax namedA | |||
|
|||
if (parameter.IsParams && parameter.Type.IsSZArray() && i + 1 == parameterCount) | |||
{ | |||
reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, argumentsCount, argsConsumedCount, this.Conversions); | |||
reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, constructorArgumentNamesOpt, argumentsCount, argsConsumedCount, this.Conversions); | |||
sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); [](start = 20, length = 77)
Should sourceIndices
be adjusted differently for a named case? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm looking into this. Thanks #Closed
public static void Main() | ||
{ | ||
var attr = typeof(Program).GetCustomAttribute<MarkAttribute>(); | ||
Console.Write(attr.B[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.Write(attr.B[0]); [](start = 8, length = 25)
Consider verifying the length of the array and the value of a
as well. #Closed
public static void Main() | ||
{ | ||
var attr = typeof(Program).GetCustomAttribute<MarkAttribute>(); | ||
Console.Write(attr.B[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.Write(attr.B[0]); [](start = 8, length = 25)
Consider verifying the length of the array and the value of a
as well. #Closed
public static void Main() | ||
{ | ||
var attr = typeof(Program).GetCustomAttribute<MarkAttribute>(); | ||
Console.Write(attr.B); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Console.Write(attr.B); [](start = 8, length = 22)
Consider verifying the value of a
as well. #Closed
Done with review pass (iteration 4). #Closed |
sourceIndices = sourceIndices ?? CreateSourceIndicesArray(i, parameterCount); | ||
reorderedArgument = GetParamArrayArgument(parameter, constructorArgsArray, constructorArgumentNamesOpt, argumentsCount, | ||
argsConsumedCount, this.Conversions, out bool foundNamed); | ||
if (!foundNamed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!foundNamed) [](start = 20, length = 16)
It doesn't feel right that we don't allocate the sourceIndices
for the case when the argument is named, but the form is not expanded.
Done with review pass (iteration 6). It looks like the issue about |
Filed #22372 for the pre-existing problem of params values getting a @AlekseyTs I think this should be good to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
test windows_debug_unit32_prtest please |
windows_debug_unit32_prtest failed with @dotnet/roslyn-infrastructure Is this a known issue? |
test windows_debug_unit32_prtest please |
@dotnet-bot test windows_debug_unit32_prtest please |
* dotnet/master: Report binding error for typed LINQ query on a type expression (dotnet#22412) clean up more refactoring Fixed tests fix test after rebase one more test CR feedback Allow taking unmanaged/pinned addresses of readonly variables. Fix typo in INotifyCompletion API in doc (dotnet#22417) Fix serialization of attribute data to account for named params argument (dotnet#22231) Create tests to cover test plans dotnet#19216 and dotnet#20127 (dotnet#22284) Checked uses of RefKind.RefReadOnly String rename Check for null before registering incremental analyzer in work coordinator. clean up Create tests to cover test plans dotnet#19216 and dotnet#20127
Customer scenario
Define attribute with
MarkAttribute(bool a, params object[] b)
and use it with[Mark(b: new object[] { "Hello", "World" }, a: true)]
.It will be incorrectly emitted as
[Mark(true, new object[] { true })]
.Bugs this fixes:
Fixes #20741
Workarounds, if any
Use attribute without naming arguments.
Risk
Performance impact
Low. This only affects the serialization of attribute data for attributes that have a
params
parameter.Is this a regression from a previous update?
No. This code was largely unchanged during the whole git history.
Root cause analysis:
The matching of arguments to parameters during overload resolution is fine. But when it comes time to prepare attribute data, we have logic to put arguments in the proper order (
GetRewrittenAttributeConstructorArguments
). It's implementation didn't handle named params arguments properly.How was the bug found?
Reported by customer
Test documentation updated?
Not applicable