kevin-montrose / Cesil

Modern CSV (De)Serializer
MIT License
65 stars 3 forks source link

Make Cesil aware of nullability annotations, and enforce certain correctness invariants implied by that #27

Closed kevin-montrose closed 3 years ago

kevin-montrose commented 4 years ago

Makes Cesil aware of null annotations, as raised in #25.

Visible changes

Commentary

I went back and forth on how much checking to do and when to do it (that is, whether to do it at "compile" time when readers/writers are created or at "runtime" when they are used), as well as how much client's should be allowed to "subvert" null handling.

Ultimately I settled on "doing it all at runtime" and "clients can subvert basically everything". I experimented with failing faster, and imposing more restrictions on clients, but ultimately it was too easy for code that "obviously" works to cause validation to fail.

The end goal is to, by default, fail quickly and helpfully if reading or writing values with subvert the constraints implied by nullable reference types I think this approach achieves that, while also allowing for clients to override or customize Cesil's behavior.

Internal notable changes

SmithPlatts commented 4 years ago

I think technically you can end up with a null this, but practically speaking it's not something that happens in sane code

Inside an object? Yes it's rare but can happen. Inside an extension method? Oh it certainly can and bit me recently...

kevin-montrose commented 3 years ago

I think technically you can end up with a null this, but practically speaking it's not something that happens in sane code

Inside an object? Yes it's rare but can happen.

Yeah, I think it's practically only when you have call instead of callvirt on a non-virtual instance method. The C# compiler will generate callvirts so you get a null check (and then the JIT will de-virtualize them), so you tend to be doing something "weird" to get into this case.

Inside an extension method? Oh it certainly can and bit me recently...

Extension methods are actually static methods, so there isn't actually a this pointer to be weird about.


I've made all the appropriate changes to this branch I think, I'll merge into vNext in the next day or so... want to give myself some time to think through any additional gotchas. Expanding tests to cover all of this definitely exposed some additional nuances, which has given me a bit of pause about just merging it.