Repository navigation
Nullable annotations #12631
Description
C# 8.0 introduces great feature - nullable reference types.
https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/
Many developers already benefit from the feature in their projects and expect that PowerShell API will be nullable annotated too.
This is a lot of work. Ilya (@iSazonov) and Staffan Gustafsson (@powercode) agreed to start the project. But we need more contributors and code reviewers.
Rain Sallow (/u/ta11ow) (@vexx32) Patrick Meinecke (@SeeminglyScience) Kirk Munro (@KirkMunro) welcome and please ask your friends and followers.
To make this work efficiently, we need Rules and Plan.
Rules
Best start is .Net team experience https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/api-guidelines/nullability.md
Main rule is that annotation PRs should not change code due to the high risk of making a bug.
I believe we should strongly follow the rule too and make separate PRs if we see a need to refactor a code and especially if we see a bug.
We could save more time if we fixed most code style issues before starting the project.
I started the work in #11916 but again I need a help with code review. (#11916 fix ~5000 issues from ~10000, and I hope to fix rest in follow some PRs. You could pull such PRs too).
Please use one pattern:
- one PR per type with name
Enable nullable: <namespace>.<type name> - enable directive before first type line (after XML comments) and restore directive after last type line without extra empty lines:
[Guid("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
#nullable enable
internal interface IInspectable { }
#nullable restore- add XML comments to non-nullable members like "This never returns null." - good documentation helps a lot
Plan
In the file System.Management.Automation-20201109114545.xlsx
( Previous System.Management.Automation-20201109114545.xlsx
all SMA PowerShell types are sorted by dependency count.
We should annotate types by groups starting from group with dependency 0 (Group0), then 1 and so on.
Main rule here is - current annotating type should have all dependencies already annotated and merged.
Status
Working on Group0
Done - most of interface types was annotated.
In process - now we can annotate structs with dependency 0
Activity
SeeminglyScience commentedon May 12, 2020
At the very least I'll definitely sign up for some reviews, I'd love to see this come together.
Steve Lee (@SteveL-MSFT) I'm not sure if y'all typically require a team member to sign off on a review before it can be merged, if so you may want to consider appointing a few folks from the community specifically for these annotation only PR's.
James Truher (@JamesWTruher) Once a lot of the this work is done it'd be amazing to see it in PowerShell Standard. Assuming your tooling doesn't already capture these (does it capture internal attribute annotations?), it would need to capture:
Also if annotations were added to the 5.1 ref lib (wishful thinking I know, but 🤞) they would all need to be embedded internal.
vexx32 commentedon May 12, 2020
I'm sure I can do some work here, for sure. 🙂
I will probably end up getting distracted with more style-related issues here and there as well, but I should be able to keep those and nullable annotation PRs completely separate.
iSazonov commentedon May 12, 2020
Rain Sallow (/u/ta11ow) (@vexx32) As for style issues I foresaw this and started # 11916. I hope we do the work first.
It is good rule for functional changes because Engine sometimes is very complex.
I can fast merge PRs with annotations while they contains only annotations because they can break nothing in functionality. (We will fix annotations later based on feedbacks for a long time.)
But we must follow strong rule - move all code changes in other PRs. If we see that a code could be refactored as result of annotations we should do this after the annotations merged.
vexx32 commentedon May 12, 2020
I'll give that one a look over, bit of a big one, but does need doing. 👍
powercode commentedon May 13, 2020
There are cases where the code wont build after annotations are added. In that case, we have to make changes, right?
But we should strive to keep changes to a minimum!
powercode commentedon May 13, 2020
In SMA, is is also useful to start at the classes at the bottom of the dependency chain.
I started with LanguagePrimitives, PSObject and the PSObject collections.
vexx32 commentedon May 13, 2020
In terms of creating a list of types to start with as Ilya (@iSazonov) mentioned, I wrote this small function to determine the inheritance chain length of a given type:
Using this, we can get a listing of the types most deeply nested first:
That's only going to be a fraction of all the types since there are a good number of assemblies to work through, but it's a start.
iSazonov commentedon May 14, 2020
Thanks Rain Sallow (/u/ta11ow) (@vexx32)! It is great!
We could simply enumerate all out assemblies with ForEach-Object
SteveL-MSFT commentedon May 20, 2020
I think adding Dongbo Wang (@daxian-dbw) and Rob Holt (@rjmholt) as reviewers (just need one of them to sign off, not both) would be sufficient. This would be great to have!
powercode commentedon May 20, 2020
Rain Sallow (/u/ta11ow) (@vexx32) We don't really care about inheritance, but dependencies between types. There will be clusters of types at different levels, and it is easier do start at the bottom.
xtqqczze commentedon Jul 6, 2020
iSazonov commentedon Sep 1, 2020
After looking this script in depth I think we need a Roslyn analyzer because we take into account all type cross-references - if Class1 uses Class2 in field, property or local the Class2 should be annotated before Class1.
Has anybody an experience with Roslyn API to create such custom analyzer?
iSazonov commentedon Nov 6, 2020
I implemented the helper tool.
It shows 2421 types only in SMA. I never thought that so many types are declared there.
There is even no way to publish this huge list in expanded form.
I have no idea how we can handle this.✈️ I suspect that many types require refactoring and new xUnit tests.
7 remaining items
xtqqczze commentedon Nov 9, 2020
I will start this work, starting at the bottom of the list with
IValidateSetValuesGeneratorand working up to the top. (in case anyone is already working top to bottom)xtqqczze commentedon Nov 9, 2020
Ilya (@iSazonov) Do we want to format the pre-processor directives to start at the beginning of the line? This would be standard indentation behaviour in Visual Studio. See #14018 (comment).
iSazonov commentedon Nov 9, 2020
If we can not to turn off the behavior in VS 2019 I agree to put this at the beginning of the line.
powercode commentedon Nov 19, 2020
We will get a lot of CodeFactor errors since we touch code that is in violation of the rules.
Can we agree to ignore these errors and only focus on annotations?
powercode commentedon Nov 19, 2020
Tick when starting work
powercode commentedon Nov 19, 2020
To help with the workflow, I have this function
powercode commentedon Nov 20, 2020
🎶It's been a hard day's night...
powercode commentedon Nov 20, 2020
I believe that was all the interfaces of depth 0
iSazonov commentedon Nov 20, 2020
Yes, we should exclude unrelated changes. If a code requires reformatting or refactoring we should make this before in separate PRs with adding tests as needed.
iSazonov commentedon Dec 11, 2020
Updated file System.Management.Automation-20201109114545.xlsx in OP.
About 50% interface types was annotated.Only 3 interface types is not annotated:
iSazonov commentedon May 20, 2021
Most of interface types was annotated.
Now we can annotate structs with dependency 0. It is ~71 struct but most of them is for p/invokes and annotations are formal. Welcome to grab!
Since MSFT team is not very active I will do fast merge since it is not critical changes.
xtqqczze commentedon Aug 25, 2021
Ilya (@iSazonov) Why is this closed? Will we not accept annotation PR anymore?
iSazonov commentedon Aug 26, 2021
xtqqczze We need to process about 20 classes daily to get the job done in a reasonable amount of time. But we see near-zero activity and complete indifference from MSFT team.
So I have no desire to keep track of dead issues.
xtqqczze commentedon Aug 23, 2025
To move forward, we should:
#nullable disableto each file.This way, all new files will automatically have nullables enabled, which will be progress at least.