Yes, completely sound nullability checking has been added to the project
via checkerframework, based on a large number of NPE bugs (1-5% depending
on how you search, but many other bugs likely attributable to
nullness-based design errors) which are extra embarrassing because NPEs
have were essentially solved, even in practice for Java, well before Beam
existed.

Checker framework is a pluggable type system analysis with some amount of
control flow sensitivity. Every value has a type that is either nullable or
not, and certain control structures (like checking for null) can alter the
type inside a scope. The best way to think about it is to consider every
value in the program as either nullable or not, much like you think of
every value as either a string or not, and to view method calls as
inherently stateful/nondetermistic. This can be challenging in esoteric
cases, but usually makes the overall code health better anyhow.

Your example illustrates how problematic the design of the Java language
is: the analysis cannot assume that `getDescription` is a pure function,
and neither should you. Even if it is aware of boolean-short-circuit it
would not be sound to accept this code. There is an annotation for this in
the cases where it is true (like proto-generate getters):
https://checkerframework.org/api/org/checkerframework/dataflow/qual/Pure.html

The refactor for cases like this is trivial so there isn't a lot of value
to thinking too hard about it.

if (statusCode.equals(Code.INVALID_ARGUMENT)
  @Nullable String desc = statusCode.toStatus().getDescription();
  if (desc != null && desc.contains("finalized")) {
    return false;
  }
}

To a casual eye, this may look like a noop change. To the analysis it makes
all the difference. And IMO this difference is real. Humans may assume it
is a noop and humans would be wrong. So many times when you
think/expect/hope that `getXYZ()` is a trivial getter method you later
learn that it was tweaked for some odd reason. I believe this code change
makes the code better. Suppressing these errors should be exceptionally
rare, and never in normal code. It is far better to improve your code than
to suppress the issue.

It would be very cool for the proto compiler to annotate enough that
new-and-improved type checkers could make things more convenient.

Kenn

On Mon, Jan 11, 2021 at 8:53 PM Reuven Lax <re...@google.com> wrote:

> I can use that trick. However I'm surprised that the check appears to be
> so simplistic.
>
> For example, the following code triggers the check on
> getDescription().contains(), since getDescription returns a Nullable
> string. However even a simplistic analysis should realize that
> getDescription() was checked for null first! I have a dozen or so cases
> like this, and I question how useful such a simplistic check it will be.
>
> if (statusCode.equals(Code.INVALID_ARGUMENT)
>     && statusCode.toStatus().getDescription() != null
>     && statusCode.toStatus().getDescription().contains("finalized")) {
>   return false;
> }
>
>
> On Mon, Jan 11, 2021 at 8:32 PM Boyuan Zhang <boyu...@google.com> wrote:
>
>> Yeah it seems like the checker is enabled:
>> https://issues.apache.org/jira/browse/BEAM-10402. I used
>> @SuppressWarnings({"nullness" )}) to suppress the error when I think it's
>> not really a concern.
>>
>> On Mon, Jan 11, 2021 at 8:28 PM Reuven Lax <re...@google.com> wrote:
>>
>>> Has extra Nullable checking been enabled in the Beam project? I have a
>>> PR that was on hold for several months, and I'm struggling now with compile
>>> failing to complaints about assigning something that is nullable to
>>> something that is not nullable. Even when the immediate control flow makes
>>> it absolutely impossible for the variable to be null.
>>>
>>> Has something changed here?
>>>
>>> Reuven
>>>
>>

Reply via email to