ForNeVeR / xaml-math

A collection of .NET libraries for rendering mathematical formulae using the LaTeX typesetting style, for the WPF and Avalonia XAML-based frameworks
MIT License
641 stars 102 forks source link

Enable C# 8's nullable annotations #230

Open ForNeVeR opened 4 years ago

ForNeVeR commented 4 years ago

After #228 is merged, we should be able to enable the new nullable reference types all over our project. It's important so we could provide the nullability information through our API for the users.

Remaining points to do:

ForNeVeR commented 4 years ago

Please also fix these TODOs:

ygra commented 4 years ago

I thought I'd start on this issue. However, a few questions arose regarding the project in general. I haven't yet read through everything and mostly followed the warnings to fix nullability issues. But of course, doing this naïvely requires a lot of nullable types.

The project multi-targets .NET 4.5.2 and .NET Core 3.0. This means that attributes that can be used to aid the compiler regarding null can only be used in one of the target frameworks, e.g. NotNullWhen. One option would be something like:

        public static bool TryGetAtom(string name,
                                      SourceSpan? source,
#if NETCOREAPP3_0
                                      [NotNullWhen(true)]
#endif
                                      out SymbolAtom? atom)
        {
            ...
        }

The other option would be to just accept that things are not as good as they could be and add ! to usages where the compiler complains.

A few things that can be null are already spelled out in comments. However, some things, like basically all atoms, seem to be nullable in many instances, but seemingly not null in others. The contagious nature of nullable types, however, causes a lot of the nullable-ness to bleed into code that seemingly assumes things to never be null. I wonder whether there's a general idea as to whether those things can be null in general.

Is this issue intended to result in a warning-free build? A bug-free build? a complete overhaul of nullability in the whole library? As mentioned, there's a few places that are very ambiguous as to whether null is really expected and desired.

ForNeVeR commented 4 years ago

Thanks for your help!

I haven't yet read through everything and mostly followed the warnings to fix nullability issues.

Yep, that's what I had in mind when writing this issue.

The project multi-targets .NET 4.5.2 and .NET Core 3.0. This means that attributes that can be used to aid the compiler regarding null can only be used in one of the target frameworks, e.g. NotNullWhen.

You could conditionally include a NuGet package with the corresponding attributes, and they should be picked up properly. See also a request for the runtime team to provide such package: https://github.com/dotnet/runtime/issues/30801.

I wonder whether there's a general idea as to whether those things can be null in general.

Not quite. One of the purposes of this task is to find the places where the nullability is ambiguous, and possibly take a closer look a bit later. All I could remember from the top of my head is that Atom.SourceSpan is supposed to be nullable almost universally. Everything else is in the gray zone I'd say.

Is this issue intended to result in a warning-free build?

Yes.

A bug-free build?

I don't think it's possible (at least it's an impractical requirement as of now), even if we only talk about the NullReferenceExceptions which could be fixed by carefully fixing all the nullability warnings.

a complete overhaul of nullability in the whole library?

Definitely not (yet).

As mentioned, there's a few places that are very ambiguous as to whether null is really expected and desired.

Yes, that's expected from a code base that old.

Generally, as the result of this task, I'd like to enable "nullability warnings as errors" flag, annotate the whole code base and shut the ambiguous places with a few ! operators (there shouldn't be too much of them, right? I hope…). At least they will become explicit and more visible. We can decide on them later; fixing all the nullability bugs is out of scope of this task.

ForNeVeR commented 4 years ago

Also, please note that I'd also accept a partial migration (i.e. covering only some files in the project) if it's impossible/too tedious to migrate the whole project at once. One practice I'd recommend to consider:

  1. Enable nullable reference types in the whole project (so any new files would automatically work with enabled nullability)
  2. Add #nullable disable to every file in the project
  3. Enable "nullability warnings as errors" flag
  4. Incrementally drop the #nullable disable and fix the warnings/errors, while possibly sending reasonably-sized PRs
ygra commented 4 years ago

Ok, I've stashed the mishmash of fixes I've had to far (down to 47 warnings) and decided to start anew with a gradual approach (I can still pick changes from the stash to avoid duplicating work). This makes it easier to commit piecemeal enablings and fixes as well as making it easier to track what nullability inclusion now brought nullable types all over everything (there were a few very viral parts that could perhaps better be tackled at the source to not let them be null to begin with).

I'll probably issue the first partial PR today or tomorrow, since I'm offline for a week after that and building a WPF library on my Linux laptop won't work so great.

One thing I noticed is that with TargetFramework=net452 the analyzer seems less smart and ignores things like Debug.Assert and other things. This made a few ! necessary. I've commented all of them for now and if someone has a better idea to solve a particular instance we can fix them in another way (! is just the quickest fix to silence the diagnostic, of course ;-)).

ForNeVeR commented 4 years ago

One thing I noticed is that with TargetFramework=net452 the analyzer seems less smart and ignores things like Debug.Assert and other things. This made a few ! necessary.

This is to be expected: the standard library for .NET 4.5.2 isn't annotated by nullable types at all. So, probably, we shouldn't care about it, and only enable nullable warnings on .NET Core 3.

I want our libraries for 4.5.2 to include the nullable type annotations in the API, though. So it's okay to ignore errors on 4.5.2 completely, and only care about the new stuff, while spreading the resulting annotations for all the artifacts we produce.

ygra commented 4 years ago

This is to be expected: the standard library for .NET 4.5.2 isn't annotated by nullable types at all.

Aaah, that's a good point I haven't considered. But yeah, in that case I'll probably make the nullability conditional to .NET Core and can get rid of a few exclamation marks and comments.

ForNeVeR commented 4 years ago

That's alright; please do whatever unblocks the future work. We'll figure out how to proceed with the legacy framework later.

ForNeVeR commented 4 years ago

I've decided to keep this issue open even after merging #277. Remaining small pieces of work are enumerated in the opening post.