You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Class-file CLASS-retention annotations (RuntimeInvisibleAnnotations) are not read: SpotBugs @CheckForNull/@NonNull on library members #161
ClassSymbolScanner.loadAnnotations reads a library member's annotations through javac's symbols (symbol.getAnnotationMirrors()). Annotations with CLASS retention are stored in a class file's RuntimeInvisibleAnnotations attribute and do not show up there. kotlinc reads them, so Kotlin sees a nullness that maddi's analysis does not.
Example: jenkins remoting's JnlpConnectionState.getStash(Class<S>) carries edu.umd.cs.findbugs.annotations.CheckForNull (RuntimeInvisibleAnnotations, checked with javap).
kotlinc infers val state = event.getStash(State::class.java) as State?.
The nullability pass never seeds it: no cause in the jenkins causes run mentions a stapler or remoting annotation, although both use @CheckForNull and @NonNull widely.
Effect on the jenkins J2K trial (2026-10-20): DefaultJnlpSlaveReceiver.kt ×6, likely also Channels.kt:50/76 and JnlpSlaveAgentProtocol4.kt:76.
Type-use annotations in RuntimeVisibleTypeAnnotations (JSpecify) are a separate path, now carried by 21b739d on ws/maddi-null.
Possible fix: read RuntimeInvisibleAnnotations of a library member from the class file itself, through the JDK ClassFile API or maddi-java-bytecode, and add those annotations next to the visible ones.
Not reproducible on current devel, and the diagnosis was off: maddi does read CLASS-retention annotations.
Front end: javac's ClassReader attaches a class file's RuntimeInvisibleAnnotations and RuntimeInvisibleParameterAnnotations to the symbol, the same way it attaches the visible ones, so ClassSymbolScanner.loadAnnotations sees them through getAnnotationMirrors(). This holds even when the annotation's own class file is not on the class path, as with a compile-only spotbugs-annotations dependency. TestClassRetentionAnnotation (maddi 00d0725d7 on ws/maddi-bug) tests both cases.
Nullability: a library method's explicit nullable annotation seeds a call's result (Contracts.libraryReturnState → NullAnnotations.explicitState, which recognises CheckForNull by simple name).
jenkins, rerun 2026-11-09 (TestJavaToKotlinJenkinsNullability): DefaultJnlpSlaveReceiver.kt now prints val state = event.getStash(State::class.java)!!. getStash in remoting 3383 carries edu.umd.cs.findbugs.annotations.CheckForNull in RuntimeInvisibleAnnotations (checked with javap), so Kotlin sees the result as nullable. DefaultJnlpSlaveReceiver.kt, Channels.kt and JnlpSlaveAgentProtocol4.kt have no kotlinc errors left, only warnings.
Activity
bnaudts commentedon Nov 9, 2026
Not reproducible on current devel, and the diagnosis was off: maddi does read CLASS-retention annotations.
ClassReaderattaches a class file'sRuntimeInvisibleAnnotationsandRuntimeInvisibleParameterAnnotationsto the symbol, the same way it attaches the visible ones, soClassSymbolScanner.loadAnnotationssees them throughgetAnnotationMirrors(). This holds even when the annotation's own class file is not on the class path, as with a compile-only spotbugs-annotations dependency.TestClassRetentionAnnotation(maddi00d0725d7on ws/maddi-bug) tests both cases.Contracts.libraryReturnState→NullAnnotations.explicitState, which recognisesCheckForNullby simple name).TestJavaToKotlinJenkinsNullability):DefaultJnlpSlaveReceiver.ktnow printsval state = event.getStash(State::class.java)!!.getStashin remoting 3383 carriesedu.umd.cs.findbugs.annotations.CheckForNullinRuntimeInvisibleAnnotations(checked with javap), so Kotlin sees the result as nullable.DefaultJnlpSlaveReceiver.kt,Channels.ktandJnlpSlaveAgentProtocol4.kthave no kotlinc errors left, only warnings.🤖 Generated with Claude Code
java-openjdk: pin that CLASS-retention annotations of a library membe…