This PR adds support for an omitisempty tag, which works like omitempty but emits additional checks for emptiness using the interface { IsEmpty() bool }.
This should have no effect on code generated without omitisempty. But when omitisempty is included on a field, the result is to perform the existing emptiness check on that field plus || checkIsEmptyf(x), which calls IsEmpty() via interface if possible. This also emits the definition of checkIsEmpty (3 lines) once at the top of each MarshalMsg or EncodeMsg method as appropriate.
I've also include some test coverage (could use more work, but I think what's here is pretty good proof this is a viable feature and doesn't break anything).
Some additional notes on the implementation:
The biggest change is the IfZeroExpr method was refactor to take an argument which triggers the omitisempty behavior. This meant changing the Elem interface, all of its implementations and all calls to it. That's where most of the work is being done to decide how to perform empty checks in which cases.
From there, the changes to encode.go and marshal.go were relatively simple. I had to add a mechanism to only output checkIsEmptyIntf once per function, but ended up being easy since encodeGen and marshalGen each provide a good place to manage this state.
There is some inefficiency in cases where someone uses omitisempty on a type that cannot possibly support an IsEmpty method (a primitive type) - in some places I was able to optimize this away and have it not perform the impossible check but a lot of cases still do this call that will always return false. I figure this is not a big deal since omitisempty is entirely opt-in on the part of the user.
This PR adds support for an
omitisempty
tag, which works likeomitempty
but emits additional checks for emptiness using the interface{ IsEmpty() bool }
.This should have no effect on code generated without
omitisempty
. But whenomitisempty
is included on a field, the result is to perform the existing emptiness check on that field plus|| checkIsEmptyf(x)
, which calls IsEmpty() via interface if possible. This also emits the definition of checkIsEmpty (3 lines) once at the top of each MarshalMsg or EncodeMsg method as appropriate.I've also include some test coverage (could use more work, but I think what's here is pretty good proof this is a viable feature and doesn't break anything).
Some additional notes on the implementation:
checkIsEmptyIntf
once per function, but ended up being easy since encodeGen and marshalGen each provide a good place to manage this state.