-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Annotate Components.Forms \ Components.Web \ Component.Server with nullable attributes #23204
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
Conversation
a28f1a8
to
454ff06
Compare
break; | ||
if (constantExpression.Value is null) | ||
{ | ||
throw new ArgumentException("The provided expression must evaluate to a non-null value."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the implementation a bit here. Creating a FieldIdentifier
with a null value will results in an argument null exception. This provides a slightly better error experience.
@@ -10,6 +10,7 @@ | |||
<DefineConstants>$(DefineConstants);ENABLE_UNSAFE_MSGPACK;SPAN_BUILTIN;MESSAGEPACK_INTERNAL;COMPONENTS_SERVER</DefineConstants> | |||
<IsPackable>false</IsPackable> | |||
<EmbeddedFilesManifestFileName>Microsoft.Extensions.FileProviders.Embedded.Manifest.xml</EmbeddedFilesManifestFileName> | |||
<Nullable>annotations</Nullable> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The vast majority of the server's code is internal implementation types and I don't want to spend time fixing that up at this point. Consequently it only has annotations on, which means you get nullability hints, but the code is not checked for correctness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this means the compiler doesn't do the check within the library but that we "trust" the annotations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. The API surface is annotated, but the implementation isn't null-checked. I want to do a follow up on these things, but it'd be useful to get the user API in first.
{ | ||
builder.Services.Configure<HubOptions<ComponentHub>>(configure); | ||
throw new ArgumentNullException(nameof(configure)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This matches the behavior of AddCircuitOptions
. Seems very strange to call this and not have it do anything by passing in a null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this follows a specific pattern, I believe we do it this way across all asp.net core. I would suggest we keep it as is for consistency.
454ff06
to
0763580
Compare
@@ -25,7 +25,7 @@ public sealed class BindInputElementAttribute : Attribute | |||
/// <param name="format"> | |||
/// An optional format to use when converting values. | |||
/// </param> | |||
public BindInputElementAttribute(string type, string suffix, string valueAttribute, string changeAttribute, bool isInvariantCulture, string format) | |||
public BindInputElementAttribute(string? type, string? suffix, string? valueAttribute, string? changeAttribute, bool isInvariantCulture, string? format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean for the type
to be null? Isn't type required? Or does it default to "text"?
In cases like these, we should consider applying defaults to the properties when we receive null-values and avoid having the code that uses these things from having to deal with null values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[BindInputElement(null, null, "value", "onchange", isInvariantCulture: false, format: null)] |
[AllowNull] | ||
[MaybeNull] | ||
[Parameter] | ||
public TValue Value { get; set; } = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it because TValue is a generic parameter that we can't do something like TValue? Value
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can only apply nullable to generics if you define them to be either reference or value types. Opaque types, not so much. This says even if you declare an InputText<string>
the value could be null, either because it was initialized as such or the value being assigned to it cannot be guaranteed to be non-null.
|
||
/// <summary> | ||
/// Gets or sets a collection of additional attributes that will be added to the generated | ||
/// <c>a</c> element. | ||
/// </summary> | ||
[Parameter(CaptureUnmatchedValues = true)] | ||
public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; } | ||
public IReadOnlyDictionary<string, object?>? AdditionalAttributes { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we use this annotation on all AdditionalAttributes parameters? I think I've seen one that is different, but I can't find it now. Just double check that these are consistent across different components.
Does the current annotation mean that the dictionary and its contents can be both null? (So you have a valid entry with a null value) What's the scenario for that last case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we use this annotation on all AdditionalAttributes parameters?
Yup, the compiler should complain if we missed some.
Does the current annotation mean that the dictionary and its contents can be both null? (So you have a valid entry with a null value) What's the scenario for that last case?
I went back and checked, and I don't think there's a scenario for the values inside the dictionary being null. I can change that. The dictionary itself would be null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see what happened here. RenderTreeBuilder
allows null-valued attributes here: https://github.com/dotnet/aspnetcore/blob/master/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L329-L333. Making https://github.com/dotnet/aspnetcore/blob/master/src/Components/Components/src/Rendering/RenderTreeBuilder.cs#L405 allow nullable attributes prevents these properties from having a non-nullable value.
It's a bit of a compromise, but we'll declare AddMultipleAttributes to accept non-null values. ComponentProperties
already produces a non-null valued dictionary, so we should be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, left a couple of questions, but it's good to go once we clarify things.
0763580
to
625ef7c
Compare
Hello @pranavkm! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
Contributes to #5680