Open lazaroclapp opened 1 year ago
We have applied fix number 3 here. Wonder if we should just close this issue and move on or leave it open and report the potentially confusing annotation to Kotlin maintainers.
IMO we can close this as long as #674 tracks the remaining known issues with our handling of varargs. We should track reporting the Kotlin issue separatley.
Context Part 1: Var args nullability on NullAway and JSpecify
Handling of nullability of var args in NullAway is admittedly a bit hacky (see #674). There is a clear answer on what the correct way to interpret
@Nullable
annotations is for type-use annotations, which we currently don't follow, namely:Means: the elements of the
varargs
array can be null, but the array reference itself is non-null (99.8% of the time, this is what we want, though it is possible in Java to pass a nullvarargs
array by e.g. passing(Object[]) null
)Whereas:
Can be used to indicate that the array
varargs
itself is@Nullable
, if absolutely needed.For declaration annotations (i.e. not type-use), the only valid position is:
And it seems reasonable to treat it equivalently to the same syntax for type-use, both for consistency and because it's more likely than not what the developer intends.
Note: We currently interpret the annotation above to mean both the array and its elements can be nullable, i.e. we read it as what, using type-use annotations, would be written as
foo(@Nullable Object @Nullable... varargs)
, but I think this is a bug/artifact of previous limitations and should be (carefully) fixed.Context Part 2: Kotlinc generated jars and restrictive
@NonNull
annotationsThe Kotlin compiler adds
@Nullable
and@NonNull
annotations to the bytecode it produces, but only declaration annotations at the full parameter level (i.e. no annotations on generics, elements of arrays, etc). So, code like:Becomes the equivalent of:
And:
Becomes the equivalent of:
(The annotations in question are
org.jetbrains.annotations.Nullable
andorg.jetbrains.annotations.NotNull
)This is generally a big help to NullAway, when configured with
-XepOpt:NullAway:AcknowledgeRestrictiveAnnotations=...
, as it can usekotlinc
added nullness information to reason about methods returning nullable or taking non-null arguments.The issue:
Unfortunately, the following Kotlin code:
Actually becomes:
Which is the equivalent of:
because
kotlinc
interprets the annotation as applying to the first parameterargs
as a whole and the var args array itself is non-null.The solution(s):
kotlinc
to change the default meaning of declaration annotations in var args to match NullAway's (desired) semantics. However, even if they agree with our interpretation, this would be a big breaking change to their jar ABI.foo(@Nullable Object... varargs)
with a declaration annotation asfoo(Object @Nullable... varargs)
with a type-use annotation. This is very ugly and confusion prone from the point of view of developers using either type of annotation, or worse, a mixture of both in different projects/targets.org.jetbrains.annotations.NotNull
when looking at a varargs argument.Neither option is perfect, but this is a rather long brain-dump to convince myself that the hack from Option 3 is the best we can do here (and to document why).