Skip to content
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

Add the list of attributes that support nullable analysis. #1191

Open
wants to merge 14 commits into
base: draft-v8
Choose a base branch
from
38 changes: 19 additions & 19 deletions standard/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,8 @@ A number of attributes affect the language in some way. These attributes include
- `System.ObsoleteAttribute` ([§22.5.4](attributes.md#2254-the-obsolete-attribute)), which is used to mark a member as obsolete.
- `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` ([§22.5.5](attributes.md#2255-the-asyncmethodbuilder-attribute)), which is used to establish a task builder for an async method.
- `System.Runtime.CompilerServices.CallerLineNumberAttribute` ([§22.5.6.2](attributes.md#22562-the-callerlinenumber-attribute)), `System.Runtime.CompilerServices.CallerFilePathAttribute` ([§22.5.6.3](attributes.md#22563-the-callerfilepath-attribute)), and `System.Runtime.CompilerServices.CallerMemberNameAttribute` ([§22.5.6.4](attributes.md#22564-the-callermembername-attribute)), which are used to supply information about the calling context to optional parameters.
- Nullable static analysis attributes (§Nullable-Analysis-Attributes).

The Nullable static analysis attributes (§Nullable-Analysis-Attributes) can improve the correctness of warnings generated for nullabilities and null states (§8.9.5).

An execution environment may provide additional implementation-defined attributes that affect the execution of a C# program.

Expand Down Expand Up @@ -863,9 +864,7 @@ For invocations that occur within declarations of instance constructors, static

#### §Nullable-Analysis-Attributes-General General

Code compiled with both nullable contexts (§8.9.4) disabled is null oblivious (§8.9.5). That means any reference type variable may be null, but null checks aren't required. Once such code is made nullable-aware, those rules change. Reference type variables should never have the null value, and such variables must be checked against null before being dereferenced.

Some APIs have more complex rules for when variables can or can't be null. In these cases, one or more of the nullable-related attributes described below can be used to express those rules. When user code is compiled in a nullable-enabled context, the compiler is required to warn when that code violates those rules. That is, these attributes help define the nullability contract for an API.
The attributes in this section are used to provide additional information to support a compiler that provides nullability and null-state diagnostics (§8.9.5). A compiler isn't required to perform any null-state diagnostics. The presence or absence of these attributes do not affect the language nor the behavior of a program. A compiler that doesn't provide null-state diagnostics shall read and ignore the presence of these attributes. A compiler that provides null-state diagnostics shall use the meaning meaning of the attributes defined in this section.
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

The code-analysis attributes are declared in namespace `System.Diagnostics.CodeAnalysis`.

Expand All @@ -883,6 +882,8 @@ The code-analysis attributes are declared in namespace `System.Diagnostics.CodeA
`DoesNotReturn` (§The-DoesNotReturn-Attribute) | This method never returns.
`DoesNotReturnIf` (§The-DoesNotReturnIf-Attribute) | This method never returns if the associated `bool` parameter has the specified value.

The following sections in §Nullable-Analysis-Attributes-General are conditionally normative.

#### §The-AllowNull-Attribute The AllowNull attribute

Specifies that a null value is allowed as an input even if the corresponding type disallows it.
Expand All @@ -909,10 +910,10 @@ Specifies that a null value is allowed as an input even if the corresponding typ
>
> ```csharp
> var v = new X();
> v.ScreenName = null; // without attribute AllowNull, get a warning
> v.ScreenName = null; // may warn without attribute AllowNull
> ```
>
> without the attribute, the compiler is required to generate a warning because the non-nullable-typed property appears to be set to a null value. The presence of the attribute suppresses that warning. *end example*
> without the attribute, the compiler may generate a warning because the non-nullable-typed property appears to be set to a null value. The presence of the attribute suppresses that warning. *end example*

#### §The-DisallowNull-Attribute The DisallowNull attribute

Expand All @@ -926,17 +927,17 @@ Specifies that a null value is disallowed as an input even if the corresponding
> public class X
> {
> [DisallowNull]
> public string ReviewComment
> public string? ReviewComment
> {
> get => _comment;
> set => _comment = value ?? throw new ArgumentNullException(nameof(value),
> "Cannot set to null");
> }
> private string _comment = "";
> private string? _comment = default;
> }
> ```
>
> The get accessor could return the default value of `null`, so the compiler warns that it must be checked before access. Furthermore, it warns callers that, even though it could be null, callers shouldn't explicitly set it to null. *end example*
> The get accessor could return the default value of `null`, so the compiler may warn that it must be checked before access. Furthermore, it warns callers that, even though it could be null, callers shouldn't explicitly set it to null. *end example*

#### §The-DoesNotReturn-Attribute The DoesNotReturn attribute
jskeet marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -949,18 +950,16 @@ Specifies that a given method never returns.
> public class X
> {
> [DoesNotReturn]
> private void FailFast()
> {
> private void FailFast() =>
> throw new InvalidOperationException();
> }
>
> public void SetState(object containedField)
> {
> if (!isInitialized)
> {
> FailFast();
> // unreachable code when not initialized:
> }
> // unreachable code:
> _field = containedField;
> }
>
Expand All @@ -969,7 +968,7 @@ Specifies that a given method never returns.
> }
> ```
>
> The presence of the attribute helps the compiler in a number of ways. First, the compiler issues a warning if there's a path where the method can exit without throwing an exception. Second, the compiler marks any code after a call to that method as unreachable, until an appropriate catch clause is found. Third, the unreachable code won't affect any null states. *end example*
> The presence of the attribute helps the compiler in a number of ways. First, the compiler can issue a warning if there's a path where the method can exit without throwing an exception. Second, the compiler can consider any code after a call to that method as unreachable, until an appropriate catch clause is found. Third, the unreachable code won't affect any null states. *end example*
Copy link
Member Author

Choose a reason for hiding this comment

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

This should be reworded. The compiler doesn't use this attribute for definite assignment nor reachability. However, it will only supress any nullability.


#### §The-DoesNotReturnIf-Attribute The DoesNotReturnIf attribute

Expand All @@ -979,6 +978,7 @@ Specifies that a given method never returns if the associated `bool` parameter h
>
> <!-- Example: {template:"standalone-lib", name:"DoesNotReturnIfAttribute"} -->
> ```csharp
> #nullable enable
> public class X
> {
> private void FailFastIf([DoesNotReturnIf(false)] bool isValid)
Expand All @@ -997,7 +997,7 @@ Specifies that a given method never returns if the associated `bool` parameter h
> }
>
> private bool isInitialized = false;
> private object _field;
> private object _field = default!;
> }
> ```
>
Expand Down Expand Up @@ -1028,13 +1028,13 @@ Specifies that a non-nullable return value may be null.

#### §The-MaybeNullWhen-Attribute The MaybeNullWhen attribute

Specifies that a non-nullable argument may be `null` when the method returns the specified `bool` value.
Specifies that a non-nullable argument may be `null` when the method returns the specified `bool` value. This is similar to the `MaybeNull` attribute (§The-MaybeNull-Attribute), but include a parameter for the specified return value.
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

#### §The-MemberNotNull-Attribute The MemberNotNull attribute

Specifies that the given member won't be `null` when the method returns.

> *Example*: The compiler analyzes constructors and field initializers to make sure that all non-nullable reference fields have been initialized before each constructor returns. However, the compiler doesn't track field assignments through all helper methods. The compiler issues a warning when fields aren't initialized directly in the constructor, but rather in a helper method. This warning is suppressed by applying the `MemberNotNull` attribute to a method declaration and specifying the fields that are initialized to a non-null value in the method. For example, consider the following example:
> *Example*: The compiler may analyze constructors and field initializers to make sure that all non-nullable reference fields have been initialized before each constructor returns. However, the compiler doesn't track field assignments through called helper methods. The `MemberNotNull` attribute specifies the fields that are initialized to a non-null value in that method. For example, consider the following example:
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
>
> <!-- Example: {template:"standalone-lib", name:"MemeberNotNullAttribute"} -->
> ```csharp
Expand Down Expand Up @@ -1096,9 +1096,9 @@ Specifies that a nullable return value will never be `null`.

Specifies that a return value isn't `null` if the argument for the specified parameter isn't `null`.

> *Example*: Sometimes the null state of a return value depends on the null state of one or more arguments. Such a method will return a non-null value whenever certain arguments aren't `null`. To correctly annotate these methods, add the `NotNullIfNotNull` attribute. Consider the following method:
> *Example*: The null state of a return value may depend on the null state of one or more arguments. Such a method will return a non-null value when certain arguments aren't `null`. To correctly annotate these methods, add the `NotNullIfNotNull` attribute. Consider the following method:
BillWagner marked this conversation as resolved.
Show resolved Hide resolved
>
> <!-- Example: {template:"code-in-class-lib-without-using", name:"NotNullIfNotNull1Attribute", replaceEllipsis:true, customEllipsisReplacements: ["return \"\";"]} -->
> <!-- Example: {template:"code-in-class-lib", name:"NotNullIfNotNull1Attribute", replaceEllipsis:true, customEllipsisReplacements: ["return \"\";"]} -->
> ```csharp
> #nullable enable
> string GetTopLevelDomainFromFullUrl(string url) { ... }
Expand Down
2 changes: 1 addition & 1 deletion tools/ExampleExtractor/ExtractorAndTesterUsersGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ template_name
;
```

The unsuffixed and suffixed versions are identical, *except* that the unsuffixed ones have using directioves for all namespaces used by examples, while the suffixed ones do not. The unsuffixed versions are used by those few examples that begin with `#undef` or `#define`, which *must* precede using directives, and which might then have explicit using directives.
The unsuffixed and suffixed versions are identical, *except* that the unsuffixed ones have using directives for all namespaces used by examples, while the suffixed ones do not. The unsuffixed versions are used by those few examples that begin with `#undef` or `#define`, which *must* precede using directives, and which might then have explicit using directives.

#### standalone-console

Expand Down
1 change: 1 addition & 0 deletions tools/example-templates/code-in-class-lib/Library.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Diagnostics.CodeAnalysis;

partial class Class1
{
Expand Down
1 change: 1 addition & 0 deletions tools/example-templates/standalone-lib/Library.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Diagnostics.CodeAnalysis;

$example-code
Loading