[Version 10.0] Feature support for CallerArgumentExpression attribute#1535
[Version 10.0] Feature support for CallerArgumentExpression attribute#1535RexJaeschke wants to merge 8 commits intodraft-10from
Conversation
Review the changes for the feature and make a few small updates.
dbed563 to
f3c17e4
Compare
|
|
||
| The type of the target parameter shall have a standard conversion from `string`. | ||
|
|
||
| > *Note:* This means no user-defined conversions from `string` are allowed, and in practice means the type of such a parameter must be `string`, `object`, or an interface implemented by `string`. *end note* |
There was a problem hiding this comment.
The "First-class Span Types" proposal will also add a standard implicit conversion from string to ReadOnlySpan<char>.
|
|
||
| - Leading and trailing white space is removed both before and after any outermost grouping parentheses are removed. | ||
| - All outermost grouping parentheses are removed both before and after any leading and trailing white space is removed. | ||
| - All other *input_element*s are retained verbatim (including white space, comments, *Unicode_Escape_Sequence*s, and `@` prefixes on identifiers). |
There was a problem hiding this comment.
Roslyn deletes some comments here:
using System;
using System.Runtime.CompilerServices;
#nullable enable
class Test
{
public static void M(int val = 0, [CallerArgumentExpression("val")] string? text = null)
{
Console.WriteLine($"val = {val}, text = <{text}>");
M(/*a*/ ( /*b*/ 1 /*c*/ + /*d*/ 2 /*e*/ ) /*f*/);
}
}lowered to:
M(3, "1 /*c*/ + /*d*/ 2");There was a problem hiding this comment.
@KalleOlaviNiemitalo At a glance, this suggests that comments before the first token (redundant parens excluded) and after the last token are treated as whitespace---so are removed---while those between tokens are preserved. This follows the rule I wrote at Line 887 above.
There was a problem hiding this comment.
White space is defined in 6.3.4 and does not include comments.
|
I'm seeing odd interactions with using System.Runtime.CompilerServices;
#nullable enable
class Test
{
string M(
int first,
[CallerArgumentExpression("vals")] string text = "N/A",
params int[] vals)
{
return text;
}
static void N()
{
string a = new Test().M(1);
string b = new Test().M(2, vals: new[] { 3 });
string c = new Test().M(4, vals: 5);
}
}Lowered to: static void N()
{
// OK: the default value of text is used, because there is no argument for vals.
string a = new Test().M(
first: 1,
text: "N/A",
vals: System.Array.Empty<int>());
// OK: the argument expression for vals is used.
string b = new Test().M(
first: 2,
text: "new[] { 3 }",
vals: new int[1] { 3 });
// What on earth?
// I expected text: "5".
string c = new Test().M(
first: 4,
text: "new Test().M(4, vals: 5)",
vals: new int[1] { 5 });
}Likewise in an attribute: using System;
using System.Runtime.CompilerServices;
class TestAttribute: Attribute
{
public TestAttribute(
[CallerArgumentExpression("val")] string text = "N/A",
params int[] val)
{
}
public char C { get; set; }
}
// [Test("Test(val: 1, C = 'x')", new int[] { 1 })]
[Test(val: 1, C = 'x')]
class Test
{
} |
|
|
||
| - Leading and trailing white space is removed both before and after any outermost grouping parentheses are removed. | ||
| - All outermost grouping parentheses are removed both before and after any leading and trailing white space is removed. | ||
| - All other *input_element*s are retained verbatim (including white space, comments, *Unicode_Escape_Sequence*s, and `@` prefixes on identifiers). |
There was a problem hiding this comment.
input_element does not include PP_Directive but AFAICT those are also retained, if they occur within the argument expression.
| - All other *input_element*s are retained verbatim (including white space, comments, *Unicode_Escape_Sequence*s, and `@` prefixes on identifiers). | |
| - All other *input_element*s and *PP_Directive*s are retained verbatim (including white space, comments, *Unicode_Escape_Sequence*s, and `@` prefixes on identifiers). |
| If an explicit argument is passed for the target parameter, no string is captured, and that parameter takes on that argument’s value. Otherwise, the text for the argument corresponding to the sibling parameter is converted to a captured string, according to the following rules: | ||
|
|
||
| - Leading and trailing white space is removed both before and after any outermost grouping parentheses are removed. | ||
| - All outermost grouping parentheses are removed both before and after any leading and trailing white space is removed. |
There was a problem hiding this comment.
I suppose "grouping parentheses" here means that the parentheses are part of the same parenthesized_expression, rather than a tuple_expression:
using System.Runtime.CompilerServices;
#nullable enable
class Test
{
public static void M(
(int, int) val,
[CallerArgumentExpression("val")] string? text = null)
{
}
static void N()
{
// text: "(1, 2)" rather than "1, 2"
M(((1, 2)));
}
}(In the grammar, a deconstruction_tuple likewise has parentheses, but that cannot be an argument_value.)
|
|
||
| > *Note:* This means no user-defined conversions from `string` are allowed, and in practice means the type of such a parameter must be `string`, `object`, or an interface implemented by `string`. *end note* | ||
|
|
||
| If an explicit argument is passed for the target parameter, no string is captured, and that parameter takes on that argument’s value. Otherwise, the text for the argument corresponding to the sibling parameter is converted to a captured string, according to the following rules: |
There was a problem hiding this comment.
Apparently, "the text for the argument" means the expression or variable_reference part of argument_value, or the attribute_argument_expression. It would be good to have an example that shows the in/out/ref part of argument_value is not included in the text.
This is Rex's adaptation of the corresponding MS proposal.
FYI, during testing, I discovered a feature that is not documented in the MS proposal: Not only is leading and trailing whitespace removed from an argument, but so too are any outermost grouping parens, with these two removal processes done repeatedly, as necessary, so an argument token set of
( ( (123) + 0) )is reduced to(123) + 0.