Skip to content
Merged
57 changes: 52 additions & 5 deletions standard/attributes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 23 Attributes

## 23.1 General

Check warning on line 4 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/attributes.md#L4

MDC032::Line length 86 > maximum 81
Much of the C# language enables the programmer to specify declarative information about the entities defined in the program. For example, the accessibility of a method in a class is specified by decorating it with the *method_modifier*s `public`, `protected`, `internal`, and `private`.

C# enables programmers to invent new kinds of declarative information, called ***attribute***s. Programmers can then attach attributes to various program entities, and retrieve attribute information in a run-time environment.
Expand Down Expand Up @@ -156,7 +156,7 @@

## 23.3 Attribute specification

Application of a previously defined attribute to a program entity is called ***attribute specification***. An attribute is a piece of additional declarative information that is specified for a program entity. Attributes can be specified at global scope (to specify attributes on the containing assembly or module) and for *type_declaration*s ([§14.7](namespaces.md#147-type-declarations)), *class_member_declaration*s ([§15.3](classes.md#153-class-members)), *interface_member_declaration*s ([§19.4](interfaces.md#194-interface-members)), *struct_member_declaration*s ([§16.3](structs.md#163-struct-members)), *enum_member_declaration*s ([§20.2](enums.md#202-enum-declarations)), *accessor_declaration*s ([§15.7.3](classes.md#1573-accessors)), *event_accessor_declaration*s ([§15.8](classes.md#158-events)), elements of *parameter_list*s ([§15.6.2](classes.md#1562-method-parameters)), and elements of *type_parameter_list*s ([§15.2.3](classes.md#1523-type-parameters)).
Application of a previously defined attribute to a program entity is called ***attribute specification***. An attribute is a piece of additional declarative information that is specified for a program entity. Attributes can be specified at global scope (to specify attributes on the containing assembly or module) and for *type_declaration*s ([§14.7](namespaces.md#147-type-declarations)), *class_member_declaration*s ([§15.3](classes.md#153-class-members)), *interface_member_declaration*s ([§19.4](interfaces.md#194-interface-members)), *struct_member_declaration*s ([§16.3](structs.md#163-struct-members)), *enum_member_declaration*s ([§20.2](enums.md#202-enum-declarations)), *accessor_declaration*s ([§15.7.3](classes.md#1573-accessors)), *event_accessor_declaration*s ([§15.8](classes.md#158-events)), *local_function_declaration*s ([§13.6.4](statements.md#1364-local-function-declarations)), elements of *parameter_list*s ([§15.6.2](classes.md#1562-method-parameters)), and elements of *type_parameter_list*s ([§15.2.3](classes.md#1523-type-parameters)).

Attributes are specified in ***attribute section***s. An attribute section consists of a pair of square brackets, which surround a comma-separated list of one or more attributes. The order in which attributes are specified in such a list, and the order in which sections attached to the same program entity are arranged, is not significant. For instance, the attribute specifications `[A][B]`, `[B][A]`, `[A, B]`, and `[B, A]` are equivalent.

Expand Down Expand Up @@ -252,10 +252,10 @@

- `event` — an event.
- `field` — a field. A field-like event (i.e., one without accessors) ([§15.8.2](classes.md#1582-field-like-events)) and an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)) can also have an attribute with this target.
- `method` — a constructor, finalizer, method, operator, property get and set accessors, indexer get and set accessors, and event add and remove accessors. A field-like event (i.e., one without accessors) can also have an attribute with this target.
- `param` — a property set accessor, an indexer set accessor, event add and remove accessors, and a parameter in a constructor, method, and operator.
- `method` — a constructor, finalizer, method, local function, operator, property get and set accessors, indexer get and set accessors, and event add and remove accessors. A field-like event (i.e., one without accessors) can also have an attribute with this target.
- `param` — a property set accessor, an indexer set accessor, event add and remove accessors, and a parameter in a constructor, method, local function, and operator.
- `property` — a property and an indexer.
- `return` — a delegate, method, operator, property get accessor, and indexer get accessor.
- `return` — a delegate, method, local function, operator, property get accessor, and indexer get accessor.
- `type` — a delegate, class, struct, enum, and interface.
- `typevar` — a type parameter.

Expand All @@ -267,6 +267,9 @@
- For an attribute on a method declaration the default target is the method. Otherwise when the *attribute_target* is equal to:
- `method` — the target is the method
- `return` — the target is the return value
- For an attribute on a local function declaration the default target is the local function. Otherwise when the *attribute_target* is equal to:
- `method` — the target is the local function
- `return` — the target is the return value
- For an attribute on an operator declaration the default target is the operator. Otherwise when the *attribute_target* is equal to:
- `method` — the target is the operator
- `return` — the target is the return value
Expand Down Expand Up @@ -513,7 +516,7 @@

#### 23.5.3.1 General

The attribute `Conditional` enables the definition of ***conditional method***s and ***conditional attribute class***es.
The attribute `Conditional` enables the definition of ***conditional method***s, ***conditional local function***s, and ***conditional attribute class***es.

#### 23.5.3.2 Conditional methods

Expand Down Expand Up @@ -667,6 +670,12 @@
>
> *end example*

#### §conditional-local-function Conditional local functions

A static local function may be made conditional in the same sense as a conditional method ([§23.5.3.2](attributes.md#23532-conditional-methods)).

A compile time error occurs if a non-static local function is made conditional.

#### 23.5.3.3 Conditional attribute classes

An attribute class ([§23.2](attributes.md#232-attribute-classes)) decorated with one or more `Conditional` attributes is a conditional attribute class. A conditional attribute class is thus associated with the conditional compilation symbols declared in its `Conditional` attributes.
Expand Down Expand Up @@ -836,6 +845,44 @@

For invocations that occur within declarations of instance constructors, static constructors, finalizers and operators the member name used is implementation-dependent.

For an invocation that occurs within a local function or an anonymous function, the name of the member method that calls that function is used.

> *Example*: Consider the following:
>
> <!-- Example: {template:"standalone-console", name:"CallerMemberName1", inferOutput:true} -->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we easily extend the example to include a lambda? Presumably this would do the trick, if we've got Action declared elsewhere:

Action anonymousFunction = () => F2();
anonymousFunction();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in the next commit.

> ```csharp
> class Program
> {
> static void Main()
> {
> F1();
> Action anonymousFunction = () => F2();
> anonymousFunction();
>
> void F1([CallerMemberName] string? name = null)
> {
> Console.WriteLine($"F1 MemberName: |{name}|");
> F2();
> }
>
> static void F2([CallerMemberName] string? name = null)
> {
> Console.WriteLine($"F2 MemberName: |{name}|");
> }
> }
> }
> ```
>
> which produces the output
>
> ```console
> F1 MemberName: |Main|
> F2 MemberName: |Main|
> F2 MemberName: |Main|
> ```
>
> This attribute supplies the name of the calling function member, which for local function `F1` is the method `Main`. And even though `F2` is called by `F1`, a local function is *not* a function member, so the reported caller of that invocation of `F2` is also `Main`. Similarly, when `F2` is called by the anonymous function assigned to `anonymousFunction`, the reported caller is the method `Main`, which calls that anonymous function. *end example*

### 23.5.7 Code analysis attributes

#### 23.5.7.1 General
Expand Down
13 changes: 10 additions & 3 deletions standard/statements.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,9 @@ A *local_function_declaration* declares a local function.

```ANTLR
local_function_declaration
: local_function_modifier* return_type local_function_header
: attributes? local_function_modifier* return_type local_function_header
local_function_body
| ref_local_function_modifier* ref_kind ref_return_type
| attributes? ref_local_function_modifier* ref_kind ref_return_type
local_function_header ref_local_function_body
;

Expand All @@ -505,18 +505,21 @@ local_function_modifier

ref_local_function_modifier
: 'static'
| 'extern'
| unsafe_modifier // unsafe code support
;

local_function_body
: block
| '=>' null_conditional_invocation_expression ';'
| '=>' expression ';'
| ';'
;

ref_local_function_body
: block
| '=>' 'ref' variable_reference ';'
| ';'
;
```

Expand Down Expand Up @@ -561,7 +564,11 @@ Unless specified otherwise below, the semantics of all grammar elements is the s

The *identifier* of a *local_function_declaration* shall be unique in its declared block scope, including any enclosing local variable declaration spaces. One consequence of this is that overloaded *local_function_declaration*s are not allowed.

A *local_function_declaration* may include one `async` ([§15.14](classes.md#1514-async-functions)) modifier and one `unsafe` ([§24.1](unsafe-code.md#241-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a `«TaskType»` type ([§15.14.1](classes.md#15141-general)). If the declaration includes the `static` modifier, the function is a ***static local function***; otherwise, it is a ***non-static local function***. It is a compile-time error for *type_parameter_list* or *parameter_list* to contain *attributes*. If the local function is declared in an unsafe context ([§24.2](unsafe-code.md#242-unsafe-contexts)), the local function may include unsafe code, even if the local function declaration does not include the `unsafe` modifier.
A *local_function_declaration* may include one `async` ([§15.14](classes.md#1514-async-functions)) modifier and one `unsafe` ([§24.1](unsafe-code.md#241-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a `«TaskType»` type ([§15.14.1](classes.md#15141-general)). If the declaration includes the `static` modifier, the function is a ***static local function***; otherwise, it is a ***non-static local function***. If the local function is declared in an unsafe context ([§24.2](unsafe-code.md#242-unsafe-contexts)), the local function may include unsafe code, even if the local function declaration doesn’t include the `unsafe` modifier.

An external local function shall have the modifier `static`, and its *local_function_body* or *ref_local_function_body* shall be a semicolon.

A *local_function_body* or *ref_local_function_body* shall be a semicolon only for an external local function.

A local function is declared at block scope. A non-static local function may capture variables from the enclosing scope while a static local function shall not (so it has no access to enclosing locals, parameters, non-static local functions, or `this`). It is a compile-time error if a captured variable is read by the body of a non-static local function but is not definitely assigned before each call to the function. A compiler shall determine which variables are definitely assigned on return ([§9.4.4.33](variables.md#94433-rules-for-variables-in-local-functions)).

Expand Down
2 changes: 1 addition & 1 deletion standard/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ For an expression *expr*, which has subexpressions *expr₁*, *expr₂*, …, *e

#### 9.4.4.24 Invocation expressions and object creation expressions

If the method to be invoked is a partial method that has no implementing partial method declaration, or is a conditional method for which the call is omitted ([§23.5.3.2](attributes.md#23532-conditional-methods)), then the definite-assignment state of *v* after the invocation is the same as the definite-assignment state of *v* before the invocation. Otherwise the following rules apply:
If the method to be invoked is a partial method that has no implementing partial method declaration, or is a conditional method or conditional local function for which the call is omitted ([§23.5.3.2](attributes.md#23532-conditional-methods), §conditional-local-function), then the definite-assignment state of *v* after the invocation is the same as the definite-assignment state of *v* before the invocation. Otherwise the following rules apply:

For an invocation expression *expr* of the form:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Sample: Attributes on Local Functions

This is taken from §23.5.6.4 The CallerMemberName attribute
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
[@0,0:4='using',<'using'>,1:0]
[@1,6:11='System',<Simple_Identifier>,1:6]
[@2,12:12=';',<';'>,1:12]
[@3,15:19='class',<'class'>,3:0]
[@4,21:27='Program',<Simple_Identifier>,3:6]
[@5,29:29='{',<'{'>,4:0]
[@6,35:40='static',<'static'>,5:4]
[@7,42:45='void',<'void'>,5:11]
[@8,47:50='Main',<Simple_Identifier>,5:16]
[@9,51:51='(',<'('>,5:20]
[@10,52:52=')',<')'>,5:21]
[@11,58:58='{',<'{'>,6:4]
[@12,68:69='F1',<Simple_Identifier>,7:8]
[@13,70:70='(',<'('>,7:10]
[@14,71:71=')',<')'>,7:11]
[@15,72:72=';',<';'>,7:12]
[@16,83:86='void',<'void'>,9:8]
[@17,88:89='F1',<Simple_Identifier>,9:13]
[@18,90:90='(',<'('>,9:15]
[@19,91:91='[',<'['>,9:16]
[@20,92:107='CallerMemberName',<Simple_Identifier>,9:17]
[@21,108:108=']',<']'>,9:33]
[@22,110:115='string',<'string'>,9:35]
[@23,116:116='?',<'?'>,9:41]
[@24,118:121='name',<Simple_Identifier>,9:43]
[@25,123:123='=',<'='>,9:48]
[@26,125:128='null',<'null'>,9:50]
[@27,129:129=')',<')'>,9:54]
[@28,139:139='{',<'{'>,10:8]
[@29,153:159='Console',<Simple_Identifier>,11:12]
[@30,160:160='.',<'.'>,11:19]
[@31,161:169='WriteLine',<Simple_Identifier>,11:20]
[@32,170:170='(',<'('>,11:29]
[@33,171:172='〔$"〕',<Interpolated_Regular_String_Start>,11:30]
[@34,173:188='〔F1 MemberName: |〕',<Interpolated_Regular_String_Mid>,11:32]
[@35,189:189='{',<'{'>,11:48]
[@36,190:193='name',<Simple_Identifier>,11:49]
[@37,194:194='}',<'}'>,11:53]
[@38,195:195='〔|〕',<Interpolated_Regular_String_Mid>,11:54]
[@39,196:196='〔"〕',<Interpolated_Regular_String_End>,11:55]
[@40,197:197=')',<')'>,11:56]
[@41,198:198=';',<';'>,11:57]
[@42,212:213='F2',<Simple_Identifier>,12:12]
[@43,214:214='(',<'('>,12:14]
[@44,215:215=')',<')'>,12:15]
[@45,216:216=';',<';'>,12:16]
[@46,226:226='}',<'}'>,13:8]
[@47,237:242='static',<'static'>,15:8]
[@48,244:247='void',<'void'>,15:15]
[@49,249:250='F2',<Simple_Identifier>,15:20]
[@50,251:251='(',<'('>,15:22]
[@51,252:252='[',<'['>,15:23]
[@52,253:268='CallerMemberName',<Simple_Identifier>,15:24]
[@53,269:269=']',<']'>,15:40]
[@54,271:276='string',<'string'>,15:42]
[@55,277:277='?',<'?'>,15:48]
[@56,279:282='name',<Simple_Identifier>,15:50]
[@57,284:284='=',<'='>,15:55]
[@58,286:289='null',<'null'>,15:57]
[@59,290:290=')',<')'>,15:61]
[@60,300:300='{',<'{'>,16:8]
[@61,314:320='Console',<Simple_Identifier>,17:12]
[@62,321:321='.',<'.'>,17:19]
[@63,322:330='WriteLine',<Simple_Identifier>,17:20]
[@64,331:331='(',<'('>,17:29]
[@65,332:333='〔$"〕',<Interpolated_Regular_String_Start>,17:30]
[@66,334:349='〔F2 MemberName: |〕',<Interpolated_Regular_String_Mid>,17:32]
[@67,350:350='{',<'{'>,17:48]
[@68,351:354='name',<Simple_Identifier>,17:49]
[@69,355:355='}',<'}'>,17:53]
[@70,356:356='〔|〕',<Interpolated_Regular_String_Mid>,17:54]
[@71,357:357='〔"〕',<Interpolated_Regular_String_End>,17:55]
[@72,358:358=')',<')'>,17:56]
[@73,359:359=';',<';'>,17:57]
[@74,369:369='}',<'}'>,18:8]
[@75,375:375='}',<'}'>,19:4]
[@76,377:377='}',<'}'>,20:0]
[@77,378:377='<EOF>',<EOF>,20:1]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(prog (compilation_unit (using_directive (using_namespace_directive using (namespace_name (identifier System)) ;)) (namespace_member_declaration (class_declaration class (identifier Program) (class_body { (class_member_declaration (method_declaration (method_modifiers (ref_method_modifier static)) (return_type void) (method_header (member_name (identifier Main)) ( )) (method_body (block { (statement_list (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F1)) ( ))) ;)) (statement (local_function_declaration (return_type void) (local_function_header (identifier F1) ( (parameter_list (fixed_parameter (attributes (attribute_section [ (attribute_list (identifier CallerMemberName)) ])) (type (nullable_reference_type (non_nullable_reference_type (class_type string)) (nullable_type_annotation ?))) (identifier name) (default_argument = (expression (null_literal null))))) )) (local_function_body (block { (statement_list (statement (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Console)) . (identifier WriteLine))) ( (argument_list (interpolated_regular_string_expression 〔$"〕 〔F1 MemberName: |〕 { (regular_interpolation (identifier name)) } 〔|〕 〔"〕)) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F2)) ( ))) ;))) })))) (statement (local_function_declaration (local_function_modifier (ref_local_function_modifier static)) (return_type void) (local_function_header (identifier F2) ( (parameter_list (fixed_parameter (attributes (attribute_section [ (attribute_list (identifier CallerMemberName)) ])) (type (nullable_reference_type (non_nullable_reference_type (class_type string)) (nullable_type_annotation ?))) (identifier name) (default_argument = (expression (null_literal null))))) )) (local_function_body (block { (statement_list (expression_statement (statement_expression (invocation_expression (primary_expression (member_access (primary_expression (identifier Console)) . (identifier WriteLine))) ( (argument_list (interpolated_regular_string_expression 〔$"〕 〔F2 MemberName: |〕 { (regular_interpolation (identifier name)) } 〔|〕 〔"〕)) ))) ;)) }))))) })))) })))))
Loading
Loading