From 3dde683586d8c3b77d967324923bb95b20d71f99 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 13:09:46 -0500 Subject: [PATCH 1/7] Add files via upload --- .../additional-files/CallerArgumentAttrM.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tools/example-templates/additional-files/CallerArgumentAttrM.cs diff --git a/tools/example-templates/additional-files/CallerArgumentAttrM.cs b/tools/example-templates/additional-files/CallerArgumentAttrM.cs new file mode 100644 index 000000000..4184b801d --- /dev/null +++ b/tools/example-templates/additional-files/CallerArgumentAttrM.cs @@ -0,0 +1,10 @@ +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}>"); + } +} From a874d2ba874a7b75c89622f554b4f25950cf1568 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 13:22:12 -0500 Subject: [PATCH 2/7] add the new attribute --- standard/attributes.md | 72 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/standard/attributes.md b/standard/attributes.md index f330acba9..fc2300ad7 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -494,7 +494,7 @@ A number of attributes affect the language in some way. These attributes include - `System.Diagnostics.ConditionalAttribute` ([§23.5.3](attributes.md#2353-the-conditional-attribute)), is a multi-use attribute class which is used to define conditional methods and conditional attribute classes. This attribute indicates a condition by testing a conditional compilation symbol. - `System.ObsoleteAttribute` ([§23.5.4](attributes.md#2354-the-obsolete-attribute)), which is used to mark a member as obsolete. - `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` ([§23.5.5](attributes.md#2355-the-asyncmethodbuilder-attribute)), which is used to establish a task builder for an async method. -- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§23.5.6.2](attributes.md#23562-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§23.5.6.3](attributes.md#23563-the-callerfilepath-attribute)), and `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§23.5.6.4](attributes.md#23564-the-callermembername-attribute)), which are used to supply information about the calling context to optional parameters. +- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§23.5.6.2](attributes.md#23562-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§23.5.6.3](attributes.md#23563-the-callerfilepath-attribute)), `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§23.5.6.4](attributes.md#23564-the-callermembername-attribute)), and `System.Runtime.CompilerServices.CallerArgumentExpressionAttribute` (§callargexpattr), which are used to supply information about the calling context to optional parameters. - `System.Runtime.CompilerServices.EnumeratorCancellationAttribute` ([§23.5.8](attributes.md#2358-the-enumeratorcancellation-attribute)), which is used to specify parameter for the cancellation token in an asynchronous iterator. - `System.Runtime.CompilerServices.ModuleInitializer` ([§23.5.9](attributes.md#2359-the-moduleinitializer-attribute)), which is used to mark a method as a module initializer. @@ -845,6 +845,76 @@ For invocations that occur within declarations of instance constructors, static For an invocation that occurs within a local function, the name of the method that calls that local function is used. Consider the following: if method `M` calls local function `F1`, which in turn calls local function `F2`, and `F2` has a parameter marked with this attribute, the method name passed to `F2` is `M`, because a local function is *not* a function member! +#### §callargexpattr The CallerArgumentExpression attribute + +The attribute `System.Runtime.CompilerServices.CallerArgumentExpression` is applied to a *target parameter*, and can result in the capture of the source-code text of a sibling parameter’s argument as a string, referred to here as the *captured string*. + +The target parameter shall have a *default_argument*. + +Consider the following method declaration: + + +```csharp +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}>"); + } +} +``` + +in which the target parameter is `text` and the sibling parameter is `val`, whose corresponding argument’s source-code text can be captured in `text` when `M` is called. + +The attribute constructor takes an argument of type `string`. That string + +- Shall contain the name of a sibling parameter; otherwise, the attribute is ignored. +- Shall omit the leading `@` from a parameter name having that prefix. + +A *parameter_list* may contain multiple target parameters. + +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* + +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. +- All other *input_element*s are retained verbatim (including white space, comments, *Unicode_Escape_Sequence*s, and `@` prefixes on identifiers). + +The captured string is then passed as the argument corresponding to the target parameter. However, if the argument for the sibling parameter is omitted, the target parameter takes on its *default_argument* value. + +> *Example*: Given the declaration of `M` above, consider the following calls to `M`: +> +> +> ```csharp +> Test.M(); +> Test.M(123); +> Test.M(123, null); +> Test.M(123, "xyz"); +> Test.M( 1 + 2 ); +> int local = 10; +> Test.M(l\u006fcal /*...*/ + // xxx +> 5); +> ``` +> +> the output produced is +> +> ```console +> val = 0, text = <> +> val = 123, text = <123> +> val = 123, text = <> +> val = 123, text = +> val = 3, text = <1 + 2> +> val = 15, text = 5> +> ``` +> +> *end example* + ### 23.5.7 Code analysis attributes #### 23.5.7.1 General From 23cdfaa6ba2155498d4d9d3a683b60ce2063f81a Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 13:29:05 -0500 Subject: [PATCH 3/7] Add new attribute type --- standard/standard-library.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/standard/standard-library.md b/standard/standard-library.md index 5c46468f3..a819065e6 100644 --- a/standard/standard-library.md +++ b/standard/standard-library.md @@ -794,6 +794,13 @@ namespace System.Runtime.CompilerServices public Type BuilderType { get; } } + [System.AttributeUsage(System.AttributeTargets.Parameter, AllowMultiple=false, + Inherited=false)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute (string parameterName); + } + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] public sealed class CallerFilePathAttribute : Attribute { @@ -1394,6 +1401,7 @@ The following library types are referenced in this specification. The full names - `global::System.Linq.Expressions.Expression` - `global::System.Reflection.MemberInfo` - `global::System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` +- `global::System.Runtime.CompilerServices.CallerArgumentExpressionAttribute` - `global::System.Runtime.CompilerServices.CallerFileAttribute` - `global::System.Runtime.CompilerServices.CallerLineNumberAttribute` - `global::System.Runtime.CompilerServices.CallerMemberNameAttribute` From 2290c2bf659a5f25606955c5c3af0f81e687dc29 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 13:45:10 -0500 Subject: [PATCH 4/7] fix md spacing --- standard/attributes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/standard/attributes.md b/standard/attributes.md index fc2300ad7..dc3e25ccc 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -900,7 +900,7 @@ The captured string is then passed as the argument corresponding to the target p > Test.M(l\u006fcal /*...*/ + // xxx > 5); > ``` -> +> > the output produced is > > ```console @@ -1262,5 +1262,3 @@ For interoperation with other languages, an indexer may be implemented using ind > Now, the indexer’s name is `TheItem`. > > *end example* - - From ebf965a7f06d126573ecd9648bb30cf0b9e70431 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 15:52:29 -0500 Subject: [PATCH 5/7] mention extension methods, and grouping parens --- standard/attributes.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/standard/attributes.md b/standard/attributes.md index dc3e25ccc..4d2af0c3c 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -849,7 +849,7 @@ For an invocation that occurs within a local function, the name of the method th The attribute `System.Runtime.CompilerServices.CallerArgumentExpression` is applied to a *target parameter*, and can result in the capture of the source-code text of a sibling parameter’s argument as a string, referred to here as the *captured string*. -The target parameter shall have a *default_argument*. +Except when it is the first parameter in an extension method, the target parameter shall have a *default_argument*. Consider the following method declaration: @@ -882,7 +882,8 @@ The type of the target parameter shall have a standard conversion from `string`. 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. +- 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). The captured string is then passed as the argument corresponding to the target parameter. However, if the argument for the sibling parameter is omitted, the target parameter takes on its *default_argument* value. @@ -896,6 +897,7 @@ The captured string is then passed as the argument corresponding to the target p > Test.M(123, null); > Test.M(123, "xyz"); > Test.M( 1 + 2 ); +> Test.M( ( ((123) + 0) ) ); > int local = 10; > Test.M(l\u006fcal /*...*/ + // xxx > 5); @@ -909,6 +911,7 @@ The captured string is then passed as the argument corresponding to the target p > val = 123, text = <> > val = 123, text = > val = 3, text = <1 + 2> +> val = 123, text = <(123 + 0)> > val = 15, text = 5> > ``` From caed4b87e4c309fced154b42d6ff3f9e2bebb046 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 15:59:53 -0500 Subject: [PATCH 6/7] correct expected console output --- standard/attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/attributes.md b/standard/attributes.md index 4d2af0c3c..32c712b5e 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -911,7 +911,7 @@ The captured string is then passed as the argument corresponding to the target p > val = 123, text = <> > val = 123, text = > val = 3, text = <1 + 2> -> val = 123, text = <(123 + 0)> +> val = 123, text = <((123) + 0)> > val = 15, text = 5> > ``` From dbed56305e103462d7487c5e2be9900722e16c98 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 19 Jan 2026 16:07:01 -0500 Subject: [PATCH 7/7] fix more console output --- standard/attributes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/attributes.md b/standard/attributes.md index 32c712b5e..4ab652c95 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -897,7 +897,7 @@ The captured string is then passed as the argument corresponding to the target p > Test.M(123, null); > Test.M(123, "xyz"); > Test.M( 1 + 2 ); -> Test.M( ( ((123) + 0) ) ); +> Test.M(( ( (123) + 0) ) ); > int local = 10; > Test.M(l\u006fcal /*...*/ + // xxx > 5); @@ -911,7 +911,7 @@ The captured string is then passed as the argument corresponding to the target p > val = 123, text = <> > val = 123, text = > val = 3, text = <1 + 2> -> val = 123, text = <((123) + 0)> +> val = 123, text = <(123) + 0> > val = 15, text = 5> > ```