Skip to content

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

Merged
5 commits merged into from
Jun 25, 2020

Conversation

pranavkm
Copy link
Contributor

Contributes to #5680

@ghost ghost added the area-blazor Includes: Blazor, Razor Components label Jun 21, 2020
@pranavkm pranavkm changed the title Annotate Components.Forms \ Components.Web with nullable attributes Annotate Components.Forms \ Components.Web \ Component.Server with nullable attributes Jun 21, 2020
@pranavkm pranavkm marked this pull request as ready for review June 22, 2020 15:06
@pranavkm pranavkm force-pushed the prkrishn/nullable-components-forms branch from a28f1a8 to 454ff06 Compare June 22, 2020 15:13
@pranavkm pranavkm requested a review from a team June 22, 2020 15:13
break;
if (constantExpression.Value is null)
{
throw new ArgumentException("The provided expression must evaluate to a non-null value.");
Copy link
Contributor Author

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>
Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

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));
Copy link
Contributor Author

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.

Copy link
Member

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.

@pranavkm pranavkm force-pushed the prkrishn/nullable-components-forms branch from 454ff06 to 0763580 Compare June 22, 2020 19:07
@@ -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)
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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?

Copy link
Contributor Author

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; }
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

@javiercn javiercn left a 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.

@pranavkm pranavkm force-pushed the prkrishn/nullable-components-forms branch from 0763580 to 625ef7c Compare June 25, 2020 16:03
@pranavkm pranavkm requested a review from a team June 25, 2020 16:07
@ghost
Copy link

ghost commented Jun 25, 2020

Hello @pranavkm!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

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 (@msftbot) and give me an instruction to get started! Learn more here.

@pranavkm pranavkm added this to the 5.0.0-preview8 milestone Jun 25, 2020
@ghost ghost merged commit c42d1a3 into master Jun 25, 2020
@ghost ghost deleted the prkrishn/nullable-components-forms branch June 25, 2020 17:36
pranavkm added a commit that referenced this pull request Jun 25, 2020
…llable attributes (#23204)

* Annotate Components.Forms \ Components.Web with nullable attributes
Contributes to #5680

* Fixup

* Fixup rebase

* Undo nullable

* Fixup
mkArtakMSFT pushed a commit that referenced this pull request Jun 25, 2020
…llable attributes (#23204) (#23355)

* Annotate Components.Forms \ Components.Web with nullable attributes
Contributes to #5680

* Fixup

* Fixup rebase

* Undo nullable

* Fixup
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants