-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add nullability to Localization #22323
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
pranavkm
commented
May 28, 2020
- Changes to support nullable for ns2.0
- Change ref assembly project to use nullable context
* Changes to support nullable for ns2.0 * Change ref assembly project to use nullable context
|
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
| </ItemGroup> | ||
| </Target> | ||
|
|
||
| <Choose> |
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.
FYI @dotnet/aspnet-build
dougbu
left a comment
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.
Pending API review of course…
| </Target> | ||
|
|
||
| <Choose> | ||
| <When Condition="'$(Nullable)' != '' AND ('$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFrameworkIdentifier)' == '.NETFramework')"> |
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.
Could you add a comment explaining why .NET Core TFMs are excluded?
| <PropertyGroup> | ||
| <TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks> | ||
| <TargetFrameworks Condition="'$(DotNetBuildFromSource)' == 'true'">$(DefaultNetCoreTargetFramework)</TargetFrameworks> | ||
| <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.
Ick, this gets ugly duplicating the src/ project's properties. Is it possible to add the (conditional) property in eng/targets/CSharp.ReferenceAssembly.props. I'm betting that would require a target in the src/ project that returns $(Nullable) and that's a major hack as well as an <MSBuild/> explosion. I hope there's something we could do but don't have a good suggestion.
Another bad suggestion 😈 would be to load the src/ project into a string and see if it contains <Nullable>annotations</Nullable> or <Nullable>enable</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.
Using <Nullable>enable</Nullable> in the ref assembly causes build errors because of all the of throw null; for instance, from the ref assembly below:
public string Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }Always using annotations seems like the right way unless we change the codegen to be null-safe.
| return rootNamespaceAttribute.RootNamespace; | ||
| } | ||
|
|
||
| return assembly.GetName().Name; |
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.
Just curious: Did the compiler complain about the previoius expression? If yes, what was it's concern?
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.
Both FullName and Name are nullable and AssemblyName's ctor does not like nulls. This had fewer null checks
| // Copyright (c) .NET Foundation. All rights reserved. | ||
| // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
|
||
| #nullable disable |
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.
You're using nullable types. Why is this needed?
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.
Some of these tests have dozens of null errors. I would like for new tests to be authored "correctly", but fixing these tests to either be correct or just litter it with null-forgiveness seems like a massive chore with low payout.
| { | ||
| /// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary> | ||
| [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] | ||
| #if INTERNAL_NULLABLE_ATTRIBUTES |
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.
Am I correct these attributes will be public when targeting .NET 5 and internal otherwise?
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 file doesn't get used in .NET Core since these types are already part of the runtime and we get them for free. They should be available as internal types in .NET Core. I copied this file as-is from dotnet/runtime with the hope that it would be easier to keep it updated as they add new annotations.
ryanbrandenburg
left a comment
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.
Overall looks good.
| public Assembly Assembly { get; } | ||
|
|
||
| public virtual string FullName => Assembly.FullName; | ||
| public virtual string FullName => Assembly.FullName!; |
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've not dug deep on the null-forgiveness operator so correct me if I'm wrong. Is this quite right though? It seems like we're saying Assembly.FullName might be null, but that's fine and we'll assign it to a string that should be non-null. Or does this actually mean "Assembly.FullName CLAIMS that it will return string?, but I know better and I'm telling you that that value will always be a non-null string"?
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.
Or does this actually mean "Assembly.FullName CLAIMS that it will return string?, but I know better and I'm telling you that that value will always be a non-null string"?
This
| : _resourceStringProvider.GetAllResourceStrings(culture, true); | ||
|
|
||
| foreach (var name in resourceNames) | ||
| foreach (var name in resourceNames ?? Enumerable.Empty<string>()) |
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.
Hmm, I'm not a huge fan of this pattern but I can't come up with a suggestion that I prefer. Maybe the better solution in this case is to not return null here. ResourceManagerStringProvider is an internal (technically pubternal) class so we shouldn't have to worry about the breaking change. Actually looking at it as-is this might be a bug which would allow a null-ref in the current state.
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.
IMO, if (resourceNames == null) { return; }. Avoids allocating an IEnumerable<string>
Although this isn't a hot path so it doesn't really matter.
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.
AFAIK, these things get cached, so it's not saving much.
|
|
||
| return rootNamespaceAttribute?.RootNamespace ?? | ||
| new AssemblyName(assembly.FullName).Name; | ||
| if (rootNamespaceAttribute != 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 like it, much clearer.
| : _resourceStringProvider.GetAllResourceStrings(culture, true); | ||
|
|
||
| foreach (var name in resourceNames) | ||
| foreach (var name in resourceNames ?? Enumerable.Empty<string>()) |
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.
IMO, if (resourceNames == null) { return; }. Avoids allocating an IEnumerable<string>
Although this isn't a hot path so it doesn't really matter.