-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Attributes for nullable annotations #29617
Comments
Should the "Maybe" values be "MayBe"? As in "this may be null", vs "this maybe null" (which has no verb) |
+1, |
I am having hard time guessing what some of the attributes do, e.g.
This is generally useful. It is not tied to nullable annotations. Should it be regular attribute, not related to NullableAnnotationAttribute? |
Where do we expect the grouping to be used? |
@jcouv's LDM notes at https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md include more in-depth descriptions from @MadsTorgersen as well as examples I compiled from around Corelib. |
Yes, [DoesNotReturn] is not directly tied to nullability. It impacts reachability in general. |
For reference, I have a local draft of a change that employs these in corelib. Appx usage counts end up being something like this:
|
Could you include attributes target in the proposal? It would clarify the intention where they can be used. |
Here's what I have in my local branch. Obviously this can be tweaked, it's just what I put in place to be able to make progress deploying the attributes across Corelib: namespace System.Runtime.CompilerServices
{
/// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class AllowNullAttribute : Attribute { }
/// <summary>Specifies that null is disallowed as an input even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class DisallowNullAttribute : Attribute { }
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class MaybeNullAttribute : Attribute { }
/// <summary>Specifies that an output will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class NotNullAttribute : Attribute { }
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class MaybeNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter may be null.
/// </param>
public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter will not be null even if the corresponding type allows it.</summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class NotNullWhenAttribute : Attribute
{
/// <summary>Initializes the attribute with the specified return value condition.</summary>
/// <param name="returnValue">
/// The return value condition. If the method returns this value, the associated parameter will not be null.
/// </param>
public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
/// <summary>Gets the return value condition.</summary>
public bool ReturnValue { get; }
}
/// <summary>Specifies that the output will be non-null if the named parameter is non-null.</summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class NotNullIfNotNullAttribute : Attribute
{
/// <summary>Initializes the attribute with the associated parameter name.</summary>
/// <param name="parameterName">
/// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null.
/// </param>
public NotNullIfNotNullAttribute(string parameterName)
{
ParameterName = parameterName ?? throw new ArgumentNullException(nameof(parameterName));
}
/// <summary>Gets the associated parameter name.</summary>
public string ParameterName { get; }
}
/// <summary>Applied to a method that will never return under any circumstance.</summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class DoesNotReturnAttribute : Attribute { }
/// <summary>Specifies that the method will not return if the associated Boolean property is passed the specified value.</summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class DoesNotReturnIfAttribute : Attribute
{
/// <summary>Initializes the attribute.</summary>
/// <param name="parameterValue">
/// The condition parameter value. Code after the method will be unreachable if the argument to the associated parameter
/// matches this value.
/// </param>
public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
/// <summary>Gets the condition parameter value.</summary>
public bool ParameterValue { get; }
}
} |
A full draft change for Corelib is here: |
Summary:
Here is the API shape we agreed on: namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.Property)]
public sealed class AllowNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.Property)]
public sealed class DisallowNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue)]
public sealed class MaybeNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Field |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue)]
public sealed class NotNullAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class MaybeNullWhenAttribute : Attribute
{
public MaybeNullWhenAttribute(bool returnValue);
public bool ReturnValue { get; }
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class NotNullWhenAttribute : Attribute
{
public NotNullWhenAttribute(bool returnValue);
public bool ReturnValue { get; }
}
[AttributeUsage(AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue, AllowMultiple = true)]
public sealed class NotNullIfNotNullAttribute : Attribute
{
public NotNullIfNotNullAttribute(string parameterName);
public string ParameterName { get; }
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
public sealed class DoesNotReturnAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class DoesNotReturnIfAttribute : Attribute
{
public DoesNotReturnIfAttribute(bool parameterValue);
public bool ParameterValue { get; }
}
} |
@bartonjs @terrajobst was |
Looping @cston. A prompt resolution on naming will be appreciated to avoid blocking his work to recognize the attributes in the compiler. Thanks |
We did discuss it at the review. It's not just you. It's the difference between "the state of this may be null" and "the state of this is 'maybe null'". I read it as the former, in which case MayBeNull is better. Others read it as the latter, in which case MaybeNull is better. I still prefer the former, but there were arguments made about the latter being a) consistent with "not null" (e.g. "the state of this is 'not null'" vs "the state of this not null", the latter of which obviously doesn't make sense), and b) being easier to type/read. No one had a strong enough preference on the former case to override the stronger opinions on the latter case. |
OK fine. Sounds good. For what it's worth, I read "if you can exchange it with 'perhaps' then maybe is potentially correct. if you can exchange it will 'could be' then may be is potentially correct". Which is basically the difference you describe. |
This has been checked in. |
I'm struggling to work out whether any of these attributes has the meaning that I quite often want of "this method doesn't return (because it'll throw an exception) if the parameter was null". For example, I'd want to be able to write: string? foo = // Get value from somewhere.
Preconditions.CheckNotNull(foo, nameof(foo)); // Ideally using CallerExpressionAttribute, of course...
Console.WriteLine(foo.Length); It feels like I want a mixture of Have I missed something, or should I file a feature request? (I can make |
I think that's what you're looking for, but it does not exist (yet?). Others said they want to see this attribute too, see dotnet/roslyn#35816 (comment) and dotnet/csharplang#2536 (comment). |
@svick: Fantastic, thanks for being more on the ball than I am. |
Filed dotnet/roslyn#37544 for tracking (post C# 8). |
@jcouv: Maybe it's just the brief description of the method that confuses me, but I don't see how |
I was wrong. The best documentation we have on nullable annotation attributes is https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md Verified with the following test: [Fact]
public void Test()
{
var source = @"
using System;
#nullable enable
public class C
{
void M2(string? x)
{
M(x);
x.ToString(); // warning
}
void M([NotNull] string? x) // when this method returns, `x` was not null
{
if (x is null) throw null!;
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)]
public sealed class NotNullAttribute : Attribute
{
}";
var comp = CreateNullableCompilation(source);
comp.VerifyDiagnostics(
// (10,9): warning CS8602: Dereference of a possibly null reference.
// x.ToString(); // warning
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(10, 9)
);
} |
I'm investigating further...
Will continue discussion in dotnet/roslyn#37544 |
The nullable reference types feature allows developers to express which APIs can accept and return
null
values. Some framework APIs, such as generic APIs, orTryXxx
methods use specific patterns that the normal type system of C# can't express. This proposal lists all the attributes.The text was updated successfully, but these errors were encountered: