null pollution - jspecify/jspecify GitHub Wiki

What is null pollution?

When an expression JSpecify defines to have a non-nullable type evaluates to null anyway, null pollution has occurred somewhere in the application. This is largely analogous to the “heap pollution” risk introduced by generics in Java 5. (Neither actually relates to the heap per se.)

Causes

Null pollution is a fact of life! It can happen for quite a variety of reasons:

  1. Some code in the application was never null-analyzed.
  2. The analyzer wasn't JSpecify-compliant.
  3. The analyzer didn't implement sound analysis (which JSpecify doesn’t mandate).
  4. The analyzer had a bug.
  5. A finding was ignored.
  6. Reflection!
  7. The developer suppressed a warning, believing this to be a special case that should be harmless or impossible in practice. For example, they write null to a non-nullable field they know is never again read.

Effects

A case of null pollution might eventually cause a surprise NullPointerException:

  1. a dereference (including unboxing, switch, etc.)
  2. an explicit runtime check like Objects.requireNonNull
  3. a runtime check inserted by a transpiler or bytecode rewriter (e.g. IDEA has this feature)
  4. crossing the border with a null-safe JVM language (like Kotlin)

But sometimes no blow-up will happen.

Due to null pollution, methods accepting a reference are still advised to check that reference at runtime (Objects.requireNonNull, etc.), so that at least the blow-up happens sooner and more definitively.

A complication

A developer might suppress a finding (Cause 7) and justify it by inspecting the surrounding source code and seeing that they are safe from Effects 1 and 2. But they might forget to think about Effects 3 and 4 (or those could come along later). The result would be an "overzealous" NPE that tries to protect against a real-world problem that was never going to happen.

Put another way, these two things are at odds with each other:

  • The practice of tools to base runtime checks on JSpecify-provided information.
  • The practice of developers to suppress warnings when their code is "logically" safe.

Our stance

  • Tool owners who insert runtime checks should be aware that sometimes JSpecify findings are meant to be suppressed. The tool should, if possible, devise a way that their own checks can be suppressed as well.
  • Developers who suppress warnings should be aware if their code might be used with tools that insert runtime checks. They might need to follow the tool's advice for how to suppress.

Occasionally a perfect storm will cause a frustrating situation for a user, but this seems like a manageable risk.

TODO: address comment