Repository navigation
Conversation
wadoon
force-pushed
the
nullness/jspecify-annotations
branch
7 times, most recently
from
November 2, 2026 01:32
05a372a to
4d3d3e3
Compare
wadoon
force-pushed
the
nullness/jspecify-annotations
branch
from
November 2, 2026 13:14
4d3d3e3 to
39412df
Compare
…e cluster 1) Class-level @NullMarked + @nullable where null is possible (232 uses). Verified by compile + spotlessCheck. Follow-up of PR #3470.
…core cluster 2) 196 files @NullMarked; precise @nullable placements (132 uses). Verified by compile + spotlessCheck. Follow-up of PR #3470.
… cluster 3) 253 files @NullMarked; @nullable on lazily-nullable fields/params/returns. Verified by compile + spotlessCheck. Follow-up of PR #3470.
…/visitor) with JSpecify (key.core cluster 4) 72 files @NullMarked. Verified by compile + spotless. Follow-up of PR #3470.
…e cluster 5) 174 files @NullMarked; io/ carries the bulk (problem loaders, replayers, parser state, FileRepo streams). Verified by compile + spotless. PR #3470.
…core cluster 6) 196 files @NullMarked; option/cache/feature chains annotated end-to-end. Verified by compile + spotless. PR #3470.
…core cluster 7) 99 files @NullMarked, 755 @nullable across the JML/NJML/spec translation chain. Verified by compile + spotless. PR #3470.
…cluster 8) 100 files @NullMarked; lazy-initialized translator state and Map.get chains annotated. Verified by compile + spotless. PR #3470.
…y (cluster 9) 117 files @NullMarked; scripts package-level @NullMarked from prior commit, @nullable gap-fills added. Verified by compile + spotless. PR #3470.
…luster 10) 151 files @NullMarked in ldt/nparser/parser/prover/control/axiom_abstraction/ taclettranslation/settings. Verified by compile + spotless. PR #3470.
…se 2 trial setup Prepares the opt-in NullAway fast-trial for the annotated modules (-DENABLE_NULLAWAY), keeping EISOP as final conformance gate. PR #3470.
…ins (-DENABLE_NULLAWAY) NullAway 0.14.x is an Error Prone BugChecker, not a javac Plugin, so the earlier -Xplugin route cannot work. Add net.ltgt.errorprone 5.1.1 + net.ltgt.nullaway 3.3.0 Gradle plugins (error_prone_core 2.49.0, nullaway 0.14.3 on the errorprone configuration). Off by default; with -DENABLE_NULLAWAY the NullAway check is ERROR severity with JSpecify mode and annotated-package scope de.uka.ilkd.key,org.key_project. EISOP (NullnessChecker) stays the final conformance gate. First trial: 155 findings across key.util/ncore/ncore.calculus/key.core. PR #3470.
…, key.ncore.calculus (Phase 2 wave 1) 55 findings cleared: Objects.requireNonNull narrowing at deref sites, honest @nullable widenings (DefaultEdge fields, PLookup.get default, ArraySort), override/param and element-nullability alignments, and @SuppressWarnings ('NullAway') bridges where NullAway cannot express the EISOP-approved contract (Location record family, type-variable array components). EISOP (the module's conformance gate) is unaffected. PR #3470.
…r 2 (Phase 2 wave 2) Cleared 101 second-order findings surfaced by the first wave's honest annotations: JavaInfo (49 + ExecutionContext runtimeInstance @nullable on IExecutionContext/ctor/field per ProgramSV precedent), SpecificationRepository (25), VariableNamer (15 incl. field-init defaults + override param alignment), TypeConverter residual (6), ConstantExpressionEvaluator (5), and made ProgramElementName's NameCreationInfo ctor params @nullable so null-name creation info is an honest contract. JDK-25 flexible-constructor syntax accidentally introduced was reverted to Java-21-legal super(...) calls. PR #3470.
…r 3 (Phase 2 wave 3) Cleared the 101 gate6 findings: TypeConverter (29, getPromotedType/getSort derefs narrowed — JavaInfo.getKeYJavaType(PrimitiveType) runtime non-null), SpecificationRepository (25, getToplevelPM wraps, updateVariables rebuilt via ProgramVariableCollection no-arg ctor + public-field assignment preserving legit-null selfVar/resultVar/excVar), TermBuilder (23, master-factory @nullable overload routing), TermLabelManager (15, honest @nullable applicationTerm chain + 7 TermLabelConfiguration fields), OriginTermLabel (6), AbstractBetaFeature (1), SVInstantiations (1), RenamingTermProperty (1). Next cascade layer surfaced (101 findings: TermBuilder 49, TacletApp 14, Node 9, NoPosTacletApp 7, TermLabelManager 6, NodeInfo 4, TermFactory 4, JavaService 3, SpecificationRepository 2, ClashFreeSubst/LDT/AbstractLDT 1 each). PR #3470.
…r 4 (Phase 2 wave 4) Cleared the 101 gate7 findings: TermBuilder (49, heapLDT/namespace/operator- lookup narrows, getOriginFactory honest @nullable), TacletApp (14) + NoPos TacletApp (7) + Node (9) + NodeInfo (4) via requireNonNull narrows, honest @nullable param widenings, eager ctor init, and 2 scoped suppressions for the deferred assumes-Instantiation widening, TermLabelManager interface boundary resolved honestly (TermLabelUpdate.updateLabels + TermLabelRefactoring params -> @nullable), TermFactory boundVars @nullable (clears ClashFreeSubst too), JavaService getConverter @nullable, LDT/AbstractLDT requireNonNull. Next cascade layer surfaced (101): TacletApp 35, OneStepSimplifier 25, Taclet 10, ProgramTransformer 6, ProgramVariable 6, LocationVariable 6, KeYJavaType 4, TacletIndex/Statistics 2, misc 5. PR #3470.
…r 5 (Phase 2 wave 5) Cleared the 101 gate8 findings: TacletApp (35)+Taclet (10, @EnsuresNonNull executor + TacletLabelHint optional fields @nullable), OneStepSimplifier (25 field-init + thread @nullable context/protocol/ruleApp) + RuleApp trio, TacletIndex/Statistics narrows, and the override-mismatch family: honest widening of 8 superclass methods over-annotated @nonnull in the campaign (ReferencePrefix.getReferencePrefix, Type.getDefaultValue, TypeReference .getReferencePrefix/getKeYJavaType, PackageReferenceContainer .getPackageReference, StatementContainer.getStatementAt, ExpressionContainer .getExpressionAt, Expression.getKeYJavaType) + PackageReference ctor/field/ getter @nullable. Next cascade layer (101): the java.ast AST subsystem — TypeConverter 15, ContextStatementBlock 13, TypeDeclaration 11, DLEmbeddedExpression 9, ArrayDeclaration 9, JavaDeclaration 6, + ~27 files with 1-4 findings. PR #3470.
…r 6, java.ast subsystem (Phase 2 wave 6) Cleared the 101 gate9 findings across the AST subsystem: TypeConverter 15 + DLEmbeddedExpression 9 (requireNonNull narrows), AST composites 40 (ContextStatementBlock incl. override-param contravariance alignment, TypeDeclaration/JavaDeclaration/StatementBlock/JavaNonTerminalProgramElement/ CompilationUnit/Throws/PackageSpecification; authorized ClassType 6-getter + ProgramPrefix.getInnerMostMethodFrame widenings), arrays+literals 24 (uniform getKeYJavaType(Services) @nullable widening across 13 literal classes, ArrayDeclaration, Literal, Operator), stragglers 13. Controller surgical fixes: TypeRef/TypeReferenceImp prefix chain and ExecutionContext/ IExecutionContext methodContext widened to @nullable; moved an illegal statement-level @SuppressWarnings in ContextStatementBlock to the method. Next cascade layer (101): java.ast statement subsystem — Try 12, MethodBodyStatement 9, EnhancedFor 9, LoopStatement 8, LabeledStatement 7, MethodDeclaration 7, + 17 files 1-4 each. PR #3470.
Level-3 module 3/4: class-level @NullMarked on all 19 main sources, @nullable on lazy fields (CachingExtension toggleAction/cachingPruneHandler, ReferenceSearchDialog applyButton/closeDialog/scrollPane) and honest widenings (ReferenceSearcher findPreviousProof -> @nullable ClosedBy, ReferenceSearchButton.updateState proof param) - all call sites already null-guard. Annotations + imports only, zero behavior change. Pre-existing unguarded derefs of @nullable node.lookup/getOpenGoal/ getSelectedProof flagged for EISOP phase, deliberately not fixed.
Level-3 module 2/4: class-level @NullMarked on all 26 main sources (12 files from the first agent pass + 15 completed/rewrapped in the resumed pass). Honest @nullable: KeYFacade findProofLine/loadProofFile/createProofObligationContainer/ replayProof envInput, CheckerData transient lifecycle fields + ProofEntry public fields + getProofEntryByContract, Node/PathNode parent chains (root = null), ProofBundleHandler bootclasspath/topLevelProjectFile (or null), DependencyGraph allSCCs/getNodeByName, DependencyChecker data, CheckConfigDialog checkWorker. Qualified types: IPersistablePO.@nullable LoadedPOContainer, IntermediatePresentationProofFileParser.@nullable Result. Annotations + imports only, zero behavior change. Pre-existing latent unguarded derefs (ProofEntry. settingsId, poContainer.getProofOblInput, DependencyGraphBuilder line.proof etc) documented for EISOP phase.
Level-3 module 1/4 (last of Level 3): class-level @NullMarked on all 40 previously-unannotated sources (2 already marked). Honest @nullable: lazy INSTANCE, solver result/preamble/translation/exception lifecycle fields, computationTime/successfulTactic, progress-dialog lazy widgets, getSolver (orElse(null)), handlerSnippets param across interface + impls, fieldSort/ nullSort from Namespace.lookup, originalSelectedNode. java.time.@nullable Duration placement (scala Duration owns the simple name). Annotations + imports only, zero behavior change. Residual unguarded derefs (lazy-field reads, Map.get flows, key.ui @nullable getter chains) documented for EISOP phase.
…ample Phase-3 infra: checkerFramework block behind -DENABLE_NULLNESS=true with -AonlyDefs scoping to each module's packages, -Xmaxerrs 10000, -Werror (same shape as key.util/key.ncore/*). Exploratory baseline: key.core.example 0 findings; keyext.ui.testgen 42 (29 init-checker artifacts + 13 real nullable flows, all pre-existing latent).
…=true, -Werror) Phase-3 first module: 42 findings to zero. 29 initialization-checker artifacts (methods called on 'this' while receiver under initialization in Swing/extension ctors - safe, fields set before the calls) -> 6 scoped @SuppressWarnings("nullness:method.invocation.invalid") on the constructors (CounterExampleAction, TestGenerationAction, TGInfoDialog, TestgenOptionsPanel, TestgenExtension, TGWorker). 13 real nullable flows -> explicit crash-equivalent NPE guards (this EISOP fork treats Objects.requireNonNull's param as @nonnull, so the masking idiom is rejected; explicit if (x == null) throw new NullPointerException matches key.core house style): Z3_CE_SOLVER x2, TGWorker originalProof (ctor/start/done), getInitConfig/ getEnv/getServices/getProofOblInput, TestgenExtension Arrays.asList + JToolBar.add. Zero behavior change on happy paths.
…exploration Phase-3 infra behind -DENABLE_NULLNESS=true (onlyDefs per module, -Xmaxerrs 10000, -Werror). Baseline: key.core.wd 125 findings; caching/exploration baselines pending wd (key.ui dependency chain pulls wd's compile).
…true, -Werror) Phase-3 example module: Main.java:170 deref of @nullable Profile.getDefaultStrategyFactory() -> crash-equivalent explicit NPE guard (house style; requireNonNull rejected by this EISOP fork). Note: the earlier '0 findings' baseline for example was a false negative - gradle dropped the task after ui.testgen's failure, so it was never checked; caught by CI's full compileTestJava run.
… -Werror) Phase-3 wd module: 125 findings in 16 files to zero. Honest @nullable first: require/ensures/modifiable + accessible/mby/represents fields (null-tolerant setters), getVisibility/posInOcc override params widened to match key.core contracts, WellDefinednessPO.createSelf @nullable. Crash-equivalent explicit NPE guards for getHeapLDT/selfKJT/getSort/replace/combine/Maps.get chains (house style; requireNonNull rejected by this EISOP fork). Ctor super() arg handling via private requireX static helpers (Java non-virtual-expr workaround). map() propagates null for the genuinely-optional term fields (accessible/mby/represents/globalDefs/axiom) - previously op.apply(null) crashed; propagation mirrors the widened getters' true nullability. 3 scoped @SuppressWarnings("nullness:method.invocation.invalid") on ctors only (WdProfile:54 super.getStandardRules, BlockWellDefinedness:74, LoopWellDefinedness:72). compileJava + compileTestJava (4 test files, onlyDefs-scoped) both green.
…ue, -Werror) Phase-3 caching module: 69 findings in 15 main files + 37 in 2 test files (onlyDefs ^de\.uka\.ilkd\.key\.(gui|proof) covers the test package) to zero. Honest @nullable: lazy lifecycle fields (CachingExtension mediator, referenceSearchButton). Crash-equivalent explicit NPE guards for getSelectedProof/getOpenGoal/lookup/node-chain/results (house style; requireNonNull rejected by this EISOP fork). 6 scoped ctor-only @SuppressWarnings("nullness:method.invocation.invalid"). Two lifecycle-safe structural changes: ReferenceSearchButton listener registration moved to registerListeners() (called immediately after ctor, before use) and ReferenceSearchTable setModel/getColumnModel moved to init() (same); ProofCachingSettings field initializers moved into ctor body (order preserved); ReferenceSearcher stream->explicit-loop rewrite + CopyBeforeDispose single-lookup filter (both semantics-identical); type-arguments-not.inferred resolved via loop rewrite instead of unsolvable @nullable streaming constraints. Test files fixed with same guard discipline, zero suppressions.
…S=true, -Werror) Phase-3 exploration module: 76 main + 30 test findings to zero (onlyDefs ^org\.key_project\.exploration covers the test package). Honest @nullable: lazy leftPanel/changeSupport lifecycle state (memoized getter), renderer value params. Crash-equivalent guards for getEnv/lookup/rule-app/parent/ branch-label chains. 9 scoped ctor-only @SuppressWarnings ("nullness:method.invocation.invalid"). Deliberate fixes: ExplorationStepsModel null-root state via EMPTY_ROOT sentinel + setRootVisible toggling (JDK sig is @nonnull, runtime supports null; all in-module root consumers updated); ExplorationRenderer graceful-degradation on GC'd WeakReference-backed nodes (a Swing renderer must never throw; previously NPE'd the tree) + tooltip null guard. Test file guard-fixed per caching precedent, all 27 assertions kept, 3/3 tests pass.
…ng + isabelletranslation + proofmanagement Phase-3 mid-tier infra behind -DENABLE_NULLNESS=true (onlyDefs per module, -Xmaxerrs 10000, -Werror). testgen onlyDefs covers both main (testgen) and test (testcase) roots; slicing test root covered by its main root; the two keyext modules have no test sources. Baselines (onlyDefs fixed to single backslash escapes - the first draft doubled them, silently matching nothing): testgen 67, slicing 179, isabelletranslation 147, proofmanagement 99.
…true, -Werror) Phase-3 mid-tier: 67 main findings to zero (plus 22 test findings under the testcase onlyDefs branch). Notable: an EISOP BugInCF crash (inconsistent sizes in visitNewArray/lub) was masking ~20 findings in TestCaseGenerator, root-caused to a double-annotated return type (@polynull + @nullable) feeding javapoet varargs -> getSafeType rewritten to a single @polynull annotation (return null legal, crash gone). Var->explicit-type fix in TestgenFacade (List.of element annotation inference). Honest @nullable widenings (TGReporter.writeln, getServices), ~40 crash-equivalent NPE guards (Map.get/ getHeapLDT/getKeYJavaType/Z3_CE_SOLVER/modelDir), 2 scoped ctor-only suppressions (TestCaseGenerator:122, OracleGenerator:80). Test sources: guard-then-assert conversions (assertNotNull has @nonnull param in this fork; NPE guard is crash-equivalent), ProgressMonitor.Empty for null-monitor (the API documents a nullable monitor), honest @nullable test fields.
…ue, -Werror) Phase-3 mid-tier: 179 main findings to zero (47 method.invocation.invalid, 42 deref, 51 arg, 18 assignment...) plus 62 test-source findings. 4 argument.type.incompatible suppressions are deliberate-null-into-legacy-API (the loader accepts null = 'not specified'; DependencyTracker ctor self- registration is the init-artifact pattern EISOP frames under a different key; DependencyGraphTest's null node is a deliberate test datum, runtime- legal via IdentityHashMap null keys) - all scoped + javadoc'd. Guard helper restructures (getAncestor(Path,levels) range-guard replacing an unchecked parent-chain). Stream->loop/tracker-local rewrites. Tests: guard-then-assert, assertions preserved, 1 scoped suppression.
…LNESS=true, -Werror) Phase-3 mid-tier: 99 findings in 20 files to zero. Honest @nullable: Proof @nullable [] loadProofFile, findProofLine @nullable ProofEntry, settingsId() @nullable Integer, picocli-injected @nullable bundlePath/output in Main. Crash-equivalent explicit NPE guards for Map.get/unboxing/getSelectedFile/getContainerType/contract/entry.proof chains (house style; requireNonNull rejected by this EISOP fork). 5 scoped argument.type.incompatible deliberate-null sites all verified runtime-legal (CheckConfigDialog + ProofManagementExt main: null Frame = standalone dialog, JDialog accepts null owner; KeYFacade replayProof + ensureSourceLoaded: loader error-origin-only / null-progressMonitor / null classpath = empty per JavaModel; HTMLReport.print: null into ST.add = empty). 3 scoped method.invocation.invalid ctor-only. ONE class-level exception: check/Node 's self-referential generic bound (T extends Comparable<T> implements Comparable<Node<T>>) trips EISOP's type-argument fixpoint - verified both alternates fail (wildcard-in-implements is javac-illegal; Comparable<? super T> also errors); scoped to single key type.argument.type.incompatible + javadoc'd.
…_NULLNESS=true, -Werror) Phase-3 mid-tier: 147 findings in 23 files to zero. Honest @nullable widenings (SledgehammerSolver rawOutput/result/computationTime, launcher shutdown resources, NumberConstantsHandler numberSymbol/services fields, IntegerOpHandler integerLDT, sort lookups) + crash-equivalent NPE guards everywhere (Map.get/parent/preamble/ poll/Matcher.group/getMessage chains; requireNonNull rewritten to guards - rejected by this EISOP fork). IsabelleRule.posInOccurrence override param widened @nullable. @KeyFor fix: getExtraSorts/getPredefinedSorts wrap keySet() in new HashSet (callers only iterate; backing maps are HashMap so no ordering guarantee changes). Lazy-getter field-flow invalidation fixes in Mediator (getFinishedSolvers returns the real array via a non-null empty-array initializer + getters; prepareDialog still assigns the real array before any outcome callback). 11 scoped suppressions with javadoc rationale: method.invocation.invalid on ctor init artifacts (8), deliberate-null argument.type.incompatible (RuleApp pio null = hierarchy-stored @nullable; Mediator actionPerformed(null) - event unused by contract; MasterHandler this-before-ctor-end with only services already assigned; ProgressPanel lazy widget getter), methodref.receiver.bound.invalid init artifact (TranslationSettings this::save).
Phase-3 big-module infra behind -DENABLE_NULLNESS=true (onlyDefs ^de\.uka\. ilkd\.key covers main + test roots, -Xmaxerrs 10000, -Werror). Baseline (real, checked): 315 main findings; test-source findings appear once main is green (fail-fast). key.ui is handled in parallel from the main worktree (separate branch, pushed after this one lands).
…true, -Werror) key.core.infflow is now NullMarked-clean under the EISOP NullnessChecker: baseline 315 findings -> 0 (compileJava + compileTestJava, -Werror). Spotless and the full module test suite (incl. the InfFlowProfileLoadingTest autopilot integration test) are green. Suppressions (all javadoc'd, scoped): 9 nullness:argument.type.incompatible (5 deliberate-null-into-legacy/Map-value sites incl. the linkStateVarsToCopies null-pair convention consumed via containsKey by InfFlowProgVarRenamer, and the macro-level canApplyTo pass-through of a null PosInOccurrence to null-tolerant base implementations), 7 nullness:override.return.invalid (getGlobalDefs x4, getMbyAtPre x2, getOrigVars), 1 nullness:override.receiver.invalid. Deliberate nulls preserved: getGlobalDefs, getMbyAtPre, getOrigVars, mapTerm mby/result/exc, ExhaustiveProofMacro cache, collectAxioms asserted-null proofConfig, linkStateVarsToCopies null pairs.
…ror) Main sources of the UI module now check clean with the EISOP NullnessChecker (3.49.5-eisop1, -AonlyDefs scoped to de.uka.ilkd.key / org.key_project.util): 2303 baseline findings in 276 files driven to zero via honest @nullable widenings, crash-equivalent explicit NPE guards, and scoped, javadoc'd suppressions (368 annotation sites, no class-level blankets, 0 added Objects.requireNonNull - rejected by this EISOP fork). Wiring (key.ui/build.gradle): EISOP NullnessChecker under -DENABLE_NULLNESS=true with the -AonlyDefs scope used by the other modules, plus a forked 4g javac worker heap - the checker OOM'd at the default 512m worker heap on this module's large source set (the bump is also required by CI's CodeQuality run). Notable fixes: - KeYGuiExtensionFacade: getContextMenuItems accepts a @nullable underlyingObject (keyboard-invoked menus pass a null position; base crashed or produced extension-dependent content, now consistently empty); getExtensionInstances drops null instances exactly like the base Objects::nonNull filter. - ProofAggregateTask.getProofEnv null-env guard mirroring BasicTask; ThreadUtilities rootGroup guard hoisted before the thread walk. - FontAwesomeSolid: explicit InputStream null guard (previous failure was an OOM artifact of the undersized checker worker). - Map.keySet() @KeyFor qualifier leaks erased via target-typed collection copies, per the isabelletranslation precedent. Known debt (CodeQuality expected RED until landed): :key.ui:compileTestJava still reports 23 findings in test sources (7 test files, untouched from base); follow-up widens the main-side contracts the tests rely on.
…DENABLE_NULLNESS=true, -Werror) Clears the known-debt note recorded in the KEY.UI LANDED ledger entry (c1c43c5): the 23 EISOP findings in the 7 untouched test files now check clean, flipping :key.ui CodeQuality from expected-RED to green. Main-side contract widening (the honest source of four test findings): - RecentFileMenu: ctor param + field accept a @nullable MainWindow (unit tests construct the menu without a real window); the two load-action deref sites (loadProofFromBundle, loadProblem) go through a new private requireMainWindow() NPE guard - crash-equivalent with the previous implicit NPE and unreachable in production (actions only fire from a real window); the JOptionPane parent site stays raw @nullable per JDK contract. Test-side fixes (assertions preserved, no behavior change): - RecentFileMenuTest: AssertJ assertThat()/isEqualTo() on the @nullable getMostRecent() suppressed scoped to the two test methods with javadoc (external library @nonnull signatures; both values null in the files-don't-exist branch, equal non-null in the exist branch). - ChaosMonkey: the getAllActions() method-ref chain (Reference::get / Objects::nonNull / Collectors.toSet) rewritten as an explicit WeakReference<Action> loop into a HashSet (EISOP rejects the chain); nullable log args wrapped in String.valueOf(...) at 3 LOGGER sites; getToolTipText() hoisted to a local to avoid the double nullable deref. - IssueDialogMessageTest: Document.insertString receives new SimpleAttributeSet() instead of a null AttributeSet (identical for plain text). - KeyboardTacletTest / ShortUniqueFileNamesTest: Map.keySet() @KeyFor qualifier leaks erased via target-typed HashSet copies (iso precedent); observable results identical (deterministic sorted stream / seeded-shuffle ArrayList, "insertion order does not matter"). - DisposedProofTreeUpdateTest: Objects.requireNonNull (rejected by this EISOP fork) converted to requireExampleDirectory()/requireProof() NPE-guard helpers - message-bearing, crash-equivalent. - AssumesInputTest: deliberate nulls into key.core ctors (BuildingException reclamation token/position, Location fileUri) suppressed method-scoped with javadoc; assertNotNull(err.position()) + double deref replaced by an explicit guard throwing AssertionError on null. - Extension: scoped class-level @SuppressWarnings("nullness:type.argument.type.incompatible") + javadoc for the self-referential generic bound (Extension<T> implements Comparable<Extension<T>>). The checker only mis-resolves T's lower bound (@KeyForBottom vs @UnknownKeyFor above NullType) under partial/incremental compilation; full module compiles were always clean. Same documented pattern as keyext.proofmanagement check/Node, applied so every build mode (including incremental and cached CI states) stays EISOP-green. Verification: canonical --rerun-tasks :key.ui:compileJava + :key.ui:compileTestJava green, and incremental builds green (the Extension flake is silenced); :key.ui:test + :key.ui:spotlessCheck green under -DENABLE_NULLNESS=true.
46 files from the key.ui EISOP landing (c1c43c5) carried pre-existing format violations (long annotated lines never re-wrapped because the commit-time gate died at :key.ui:compileTestJava before spotlessCheck ran). This commit is the mechanical `:key.ui:spotlessApply` output only: line wrapping and import order, zero semantic change (no checker delta by construction; the module was re-gated green afterwards, incl. spotlessCheck).
… key.core files CI's formatting job (top-level spotlessCheck) flagged 4 key.core files whose import blocks carried JMLtk-rebase artifacts: a redundant blank line between the javaparser and jspecify import groups (TransactionStatement, AbstractOperationPO) and unused org.jspecify.annotations.NonNull imports (JmlDocRemoval, AuxiliaryContractBuilders - @NullMarked covers the file). Mechanical spotlessApply output; spotlessCheck clean; no semantic change.
… stub dir, heap) - key.core checkerFramework block behind -DENABLE_NULLNESS with -Werror, -Xmaxerrs 10000, module-wide -AonlyDefs (de.uka.ilkd.key, org.key_project.util) - -PnullWave=<FQCN,...> scopes -AonlyDefs to per-class waves for fast iteration - shared stub dir key.util/src/main/checkerframework for all modules (drops the explicit permit-nullness-assertion-exception.astub suffix), new javaparser astubs; retire java/beans/PropertyChangeSupport.astub - 6g daemon and forked javac worker heaps: the checker OOMs at defaults on key.core's ~1600-file source set - suppress the legacy -Ajavadoc.packages option in nullness builds (-Werror rejects the unrecognized -A warning once a processor is active)
…error) Full-module EISOP NullnessChecker conformance for key.core main sources (argument/assignment/deref waves consolidated; per-class -PnullWave waves merged into the module-wide gate). Honest @nullable widenings, explicit crash-equivalent guards, and scoped javadoc'd suppressions only; no behavior change.
The nullness annotations shifted line numbers in the pipeline builder sources (cluster 4 and the EISOP drive), so the 'Created by X.java:N' comments in the generated outputs drifted; the 76 affected expected files are regenerated from the actual pipeline output. Verified the diff is purely line-number updates and pure line moves.
Mirrors the checkerFramework job with Error Prone's NullAway in JSpecify mode. Left continue-on-error until key.core is NullAway-clean: the gate run on this head reports ~3.3k findings, most of which predate the rebase (the wave campaigns deepened the analysis instead of exhausting it, cf. docs/plan-jspecify-nullness.md). Flip the flag once the findings are driven to zero.
The NullnessChecker (EISOP) build hangs during compilation and burns the whole job timeout on CI. Disable the job (reversible via if: true); the operating nullness gate is NullAway for now.
This was referenced Nov 3, 2026
Do not infer array initializers again while computing their target type
eisop/checker-framework#2487
Merged
wadoon
force-pushed
the
nullness/jspecify-annotations
branch
from
November 3, 2026 09:37
39412df to
9ac56c6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
This pull request resolves #3470 (continues the goal of PR #3470 "Nullness Type System for
key.core" with a workflow that avoids its speed problem).Intended Change
Apply JSpecify nullness annotations to the whole KeY codebase, module by module, following the dependency chain and module size; within each module, package by package.
Annotated code gets JSpecify
@NullMarkedscopes and precise@Nullabletype-use annotations (correct placement for arrays, nested types, generic bounds) so that downstream consumers — Kotlin, IDEs, and nullness checkers — see a real API contract. Behavior does not change.Status at a Glance
-DENABLE_NULLAWAY, opt-in)-DENABLE_NULLNESS=true)39412df61f9ac56c631dPlan
1 · JSpecify annotations — done
key.util,key.ncore,key.ncore.calculus,key.ncore.matcherkey.core(1573 files) — 10 package clusters (waves 1–3), compile + spotless green per waveinfflow,testgen,wd,example,slicing,exploration,ui,isabelletranslation,proofmanagement,caching,ui.testgenmain(76167728c6); head9ac56c631d— all 67 commits linear (no merges), formatting clean (spotlessCheckgreen on all modules)KeYJavaType→TypeReferencechange) and the branch's JSpecify annotations re-applied (@NullMarked/@Nullable, crash-equivalent guards). Rebase-marker contamination (NullAway waves 3/4/5/11) and latent resolution bugs were repaired inside their owning commits (no standalone spotless commits).spotlessCheck+compileJava(all modules) andtestClasses(key.core,key.core.infflow,key.ui). Per user directive the EISOP gate is dropped — its CI compile hung 6h on GitHub Actions (see §Status); NullAway is the operating nullness gate and CI re-validation of the rebased head is in progress..keyloader crashed on legal schematic inputs because branch-added guards were not crash-equivalent to main's pass-through semantics — zero-arity calls (ExpressionBuilder, main maps a missing argument list toNO_SUBTERMS), schema declarations without concreteKeYJavaType(JP2KeYConverter.visitVariableSpecification/visitNoMap, resolved at taclet instantiation), bodyless schematic methods like$allocate(SLEnvInput, the AST walkers tolerate a null root), and\datatypetaclets without parse-time builder (TacletPBuilder, main deliberately stores a null builder). All restored to main semantics with scoped, javadoc'd suppressions; null-message hardening on the error-reporting path (ExceptionTools). A second regression class was found the same way: several wave commits replaced main's position-preservingnew Location(nullableFile, position)withLocation.UNDEFINEDwhenever the source had no file URI (in-memory inputs), silently destroying error positions inBuildingIssue,SyntaxErrorReporter,PreParser,PositionedString,KeyAst,JavaSourceLocations,SVInstantiationExceptionWithPosition,TextualJMLConstruct, andSLTranslationException— all restored to main semantics (MultiSyntaxErrorTest,KeYParserExceptionTest,IssueDialogMessageTestgreen again). A third class: the testgen drive turned main's tolerated-null Z3 version logging into a hardNullPointerExceptioninTGMain, failing all test-case generation whenever the solver version could not be parsed — restored to main's logging behavior (amended into the testgen drive1038f066d2). Commit4397a036c0regenerates the 76 pipeline expected files ("Created by" line-number drift and pure line moves only).2a303cd83a(bisect-verified to that commit): (a) the drive'sObjects.requireNonNull(uic)inFinishSymbolicExecutionUntilMergePointMacro.doPostProcessingcrashed on the nullUserInterfaceControlthat macros legitimately receive in headless runs (e.g. tests); the surrounding emptycatchswallowed the NPE, the post-processing steps silently stopped, and the resulting merge-partner program counters no longer matched — restored to main's pass-through with a scoped suppression (MergeRuleTestsincl.testDoManualGcdProof,LoopScopeInvRuleTestsgreen); (b) the drive hardened proof-replay/pruning sites where main deliberately tolerates nulls (ProgVarReplaceVisitor's null-tolerant term replacement —performActionOnLoopInvarianton a null free invariant,ProofPruner.prune's possibly-null rule-app pass-through,IntermediateProofReplayer's nullable parse-tree enqueue and null join-procedure lookup) — restored to main semantics with scoped suppressions (TestGoal,TestCopyingReplayer, MT-determinism suites, cachingProofCachingSingleRunTest/TestReferenceSearcher, and the fullkeyext.slicingmodule green).2 · NullAway fast trials — done (opt-in
-DENABLE_NULLAWAY)121d45465c)bba4686bc1…0b9c490ec9); each fix deepened the analysis and surfaced new downstream findings —key.corewas never fully NullAway-clean (see the parked count below)requireNonNullin proof paths restored to main semantics via honest@Nullablewidenings; verified locally (ScDeterminismTest14/14,InfFlowProfileLoadingTest3/3,IssueDialogMessageTest2/2)e006949b24: earlier counts were truncated at javac's 100-error cap; an uncapped scan shows a longkey.coretail (4,024 findings / 493 files) — parked; EISOP was the conformance gate until it was dropped (see §Status)a9cf52f32e,continue-on-error): the gate run reports 3,358 findings on the head, 3,319 of which already exist on the pre-rebase tip; blocking gating waits for the fix campaign3 · EISOP conformance (
-DENABLE_NULLNESS=true -Werror) — modules donekeyext.ui.testgencac81640f9key.core.examplecd4351dabdkey.core.wd0211349ef0keyext.caching9231ccf268keyext.exploration7772d6eb32key.core.testgen1038f066d2keyext.slicing40caa77220keyext.proofmanagement6f8d0a4c5bkeyext.isabelletranslation0796e759b3key.core.infflowa1a9aff15fkey.ui8d890495e0(+ test fixes23a73e9bf6, format72709e382d)4 · Final gates — ⬜
key.coreNullAway findings to zero (3,358 on head — 3,319 pre-date the rebase; see §2), then flip the CInullawayjob fromcontinue-on-errorto a blocking gate9ac56c631d)Type of pull request
Ensuring quality
compileJavaandspotlessCheck; NullAway runs per module before that module is marked done (the EISOP gate is dropped — see §Status).Additional information and contact(s)
This PR is tracked in
docs/plan-jspecify-nullness.mdin this branch. Work is executed in parallel by package clusters; each cluster is committed and pushed as it lands.The contributions within this pull request are licensed under GPLv2, or (at your option) any later version, for inclusion in KeY.
Previous (verbose) description — kept for the audit trail
Plan
key.util,key.ncore,key.ncore.calculus,key.ncore.matcher(already annotated + EISOP-checked)key.core(1573 files) — fully annotated in 10 package clusters (waves 1–3), compile + spotless green per wavekey.coreNullAway trials in progress — wiring committed (Error Prone plugins,-DENABLE_NULLAWAY); 155 initial findings driven to zero in dependency order across 8 committed waves (each wave = one honest-@Nullable-widening cascade layer, 101 findings per layer): wave 1 (key.util,key.ncore,key.ncore.calculus,Proof,Goal,Services,NamespaceSet—c034d1ef5a,3d1b13bbce,de2b8eb1c1), wave 2fc8a974673(JavaInfo/SpecificationRepository/VariableNamer/TypeConverter/ExecutionContext), wave 328ba9d31e9(TypeConverter/SpecificationRepository/TermBuilder/TermLabelManager chain), wave 4d346422960(TermBuilder 49, TacletApp/Node, TermLabelUpdate/TermLabelRefactoring contract widenings), wave 52f87109e63(TacletApp/Taclet/OneStepSimplifier + 8 honest superclass contract widenings), wave 6f400cd412d(java.ast subsystem: AST composites, 13 literal classes, TypeConverter/DLEmbeddedExpression), wave 74e583b5989(statement/declaration subsystem) and wave 84446e87218(reference leaves, KeyAst parser bridges,NamedModelElement/NamedProgramElementbase-widening)), wave 9366a971208(AuxiliaryContract honest@NullableVariables.self/result/returnFlag+Contract.OriginalVariablescompanion, statement tail, ccatch/misc, pp/logic stragglers), wave 10785930667a(JmlIO cluster, speclangContractgetter widenings incl.getProofObl, pp position tables + print-filter verdicts, settings/proof cluster incl.AbstractSettings.firePropertyChange, rule/logic/ast-op incl. the wave-4-deferredTacletApp"assumes"-instantiation widening resolution +TypeReference.getName/IForUpdates.getUpdates/ILoopInit.getInitssuperclass widenings). wave 11d7c3df557b(loader/transform ripple:KeYJavaASTFactory,CreateArrayMethodBuilder,JP2KeYTypeConverter,ProgramSVSort,TransformationPipelineServices+ residuals), wave 1253f5275d2c(contract-rule consumers +TermLabelManagerrefactor-chain optional args +TransformerCacheinit), wave 1374955eff28(rule-app cluster:UseDependencyContractApp/ContractRuleApp/AbstractContractRuleApp; speclang builders:ContractFactory/RepresentsAxiom;instantiateLabelschaintacletTerm→ optional — javadoc "null in case of built in rules"). wave 142a482f8a97(contract internals:FunctionalOperationContractImpl70-finding layer + LDT/rule-apps/java-info residuals —HeapLDT,ProgramMethod,KeYProgModelInfo,ObserverFunction,UseDependencyContractRule), wave 15e9c8d4cc06(stop-and-fix CI wave: the full Tests workflow exposed runtime-NPE regressions where the branch had converted main-tolerated nulls into fatalrequireNonNulls in proof paths — all restored to main-equivalent semantics with honest@Nullablewidenings:OneStepSimplifierjustification/restriction optional again,Taclet.getNameCorrespondent→@Nullable,@Nullable ExecutionContextacross theExpressiontree +TypeConverterpass-through,UseOperationContractRule.getStaticPrefixTypepass-through,Contract/OperationContractper-heap getters (getPre/getPost/getFreePre/getFreePost/getModifiable/getFreeModifiable/getDep/…) honestly@Nullablewith raw map returns inFunctionalOperationContractImpl(heaps without a clause, e.g.savedHeap, are legitimate), andBuildingIssue.getLocationposition preservation fixing the key.ui underline offset formemory:/fragments. Locally verified:ScDeterminismTest14/14,InfFlowProfileLoadingTest3/3,IssueDialogMessageTest2/2; NullAway gate at 101 findings, all remaining in the next-wave smt/settings/LDT layer); follow-upa1217e63cb— further wave-15 regressions restored to main semantics:TypeConverter.getPromotedTypeString-root fix (Objects.equalsagainst the possibly-absentjava.lang.StringKeYJavaType, replacing a newrequireNonNullthat broke javacard-profile loads),ProgramSV.check+ProgramSVSort(canStandForbase + 5 overrides +getKeYJavaTypehelper) widened to@Nullable ExecutionContext, andPrettyPrinter.performActionOnTypearray-base-type pass-through (arequireNonNullfrom wave 9 crashedJavaService.readCompilationUnitsprinting; main prints a missing base Java type as"unknown", kept the guard only where main would itself NPE)), wave 1604571f7b6f(key.core smt/settings/LDT/proof-mgt cluster driven to zero: honest@Nullablewidenings —Model.processAnyValue/getAliasedName,SMTTerm.toListparam, LDTisResponsiblesubparams, Boolean/CharList/HeapLDT return widenings — plus main-crash-equivalentObjects.requireNonNullat every unconditional deref (LocSetLDT/SeqLDTinit lookups,SMTTerm/SMTFunction/SMTProblem,ProofCorrectnessMgt,ProblemTypeInformation,NodeReplacement,ProofIndependentSettings,AbstractPropertiesSettings) and three root-cause contract widenings replacing deferred suppressions:JavaInfo.getAttribute(String,String)@NullableclassName,FieldReferencectors@Nullableprefix,OutputStreamProofSaver.printTerm@NullableServices. Three documented suppressions remain (AbstractPropertiesSettings.fromObject,Model.processArrayValues/processSeqValues) whose honest fixes cascade into out-of-cluster sinks). Gate-count correctionca0d82d0d3: all prior NullAway counts were truncated at javac's default 100-error cap — every full-chain run aborted after the first 100 errors, so the "155"/"101-per-layer" counts were compilation-order slices, not totals; trial builds now pass-Xmaxerrs 10000(as key.util/ncore do). The wave-16 targets are clean-compile-verified fixed; an uncapped full scan reports the true remaining key.core tail at 4,024 findings across 493 files (identical across independent 2g- and 8g-heap runs, with the daemon crashing/hanging only after the full set is emitted) — parked as a known follow-up (long tail, not a near milestone; CI compilation + Tests stay green throughout; NullAway remains an opt-in trial, EISOP the conformance gate).key.core.wd,key.core.testgen,key.core.infflow,key.core.example(Level 1 —f42ef27822,018d31f7dc,936983c4ff,381ce0d467)keyext.slicing(3e6eb4d23f),keyext.exploration(51d974a89d), thenkey.ui(372 files,1a4b226af9) — all@NullMarked+ compile/spotless green per modulekeyext.isabelletranslation(42,bc043be029),keyext.proofmanagement(26,4de17d25fd),keyext.caching(19,2c83d7d810),keyext.ui.testgen(7,0ca5515005) — all@NullMarked, gate green per module (one JSpecify placement fix:java.time.@Nullable Duration, scalaDurationowns the simple name)keyext.ui.testgenwired + driven EISOP-clean (-DENABLE_NULLNESS=true,-Werror) — 42 findings to 0 (ba4764ebba; 29 init-checker artifacts → 6 scoped@SuppressWarnings("nullness:method.invocation.invalid"), 13 real nullable flows → explicit crash-equivalent NPE guards; note: this EISOP fork rejectsObjects.requireNonNull(nullable));key.core.examplefixed EISOP-clean (cf84fd285d— Main.java:170@Nullable getDefaultStrategyFactory()guard; earlier "0 findings" was a false negative: gradle dropped example's task after ui.testgen's failure, never checked — caught by CI). Wiringda4c86b82epushed with wd's fixes;key.core.wdEISOP-clean (c5b7859b9c— 125 findings to 0: honest @nullable widenings incl. getVisibility/posInOcc/rule-app overrides, explicit NPE guards for getHeapLDT/selfKJT/getSort/replace/combine/Map.get chains, ctor-super() requireX static helpers, map() null-propagation for genuinely-optional term fields; 3 scoped constructors-only suppressions). wd 4 test files compileTestJava-green (onlyDefs scoped). NEXT:keyext.explorationEISOP-clean (acb71b3e7e— 76 main + 30 test findings to 0; 9 scoped ctor-only suppressions; EMPTY_ROOT sentinel null-root model + setRootVisible, graceful-degradation GC'd-node renderer, memoized changeSupport getter; 27/27 assertions, 3/3 tests). ✅ SMALL-MODULES BATCH COMPLETE: ui.testgen (7)ba4764ebba, example (1)cf84fd285d, wd (16)c5b7859b9c, caching (19)40690a589d, exploration (13)acb71b3e7e— all-DENABLE_NULLNESS=true -Werror-green incl. test compiles; CodeQuality should be green again onacb71b3e7e. REASSESS before key.ui (372) / infflow (98) — remaining candidates: testgen (41), slicing (37), isabelletranslation (42), proofmanagement (26). MID-TIER BATCH: wired locally (5fd5d74979, amended, unpushed).\\\\.in the file = literal-backslash regex) → checker matched nothing → vacuous "0 findings" for all four, caught by a deliberate @Nullable-deref probe (wd probe DID flag → mechanism fine; iso probe didn't → block-specific). Fixed (single\., audited all 12 onlyDefs blocks in repo). Real baselines after fix: testgen 67 (11 files), slicing 179, isabelletranslation 147, proofmanagement 99 (~492 total). Pair-1 DONE + pushed (wiring5fd5d74979rides along):key.core.testgenEISOP-clean (32a948ff4c— 67 main + 22 test → 0; EISOP BugInCF crash rooted to double-annotatedgetSafeTypereturn (@PolyNull+@Nullable) → single@PolyNull;var→explicit type in TestgenFacade; ~40 explicit NPE guards; 2 scoped ctor-only suppressions),keyext.slicingEISOP-clean (d49c0e4f2a— 179 main + 62 test → 0; 15 suppressions TOTAL incl. 4 documented deliberate-nullargument.type.incompatibleexceptions validated runtime-legal: SlicingProofReplayer:151 + SlicingLeftPanel:533 null-into-loader ("not specified") + DependencyTracker:102 ctor self-registration + DependencyGraphTest:27 null-node IdentityHashMap datum;getAncestor(Path,int)helper; guard-then-assert in tests, assertions preserved). Review checks passed (suppression audit incl. all 4 arg-incompatible justifications, assert parity, lifecycle-equivalence for struct-changes, behavior scans). CI CodeQuality re-running ond49c0e4f2a— expected RED until iso/pm land (their wiring is pushed). Pair-2 DONE + pushed:keyext.proofmanagementEISOP-clean (70366bd569— 99 → 0 in 20 files; 5 documented deliberate-null argument.type.incompatible sites all runtime-verified: JDialog null-parent × 2 (standalone dialog + centered), loader error-origin-only/null-progressMonitor/null-classpath=empty per JavaModel, ST.add null=empty; 3 ctor-only method.invocation.invalid; ONE class-level exception:check/Node.javaself-referential generic boundT extends Comparable<T> implements Comparable<Node<T>>trips EISOP type-argument fixpoint — INDEPENDENTLY VERIFIED both alternates fail (Comparable<? super Node<T>>javac-illegal in implements;T extends Comparable<? super T>same error), scoped to single keytype.argument.type.incompatible+ javadoc; the agent's 'previously approved' claim was false — this is a newly reviewed exception recorded here),keyext.isabelletranslationEISOP-clean (450e9f0425— 147 → 0 in 23 files; 11 scoped suppressions incl. 4 deliberate-null argument.type.incompatible (RuleApp pio-null = hierarchy @nullable; Mediator actionPerformed(null) event-unused; MasterHandler this-before-ctor-end services-only; ProgressPanel lazy-widget), methodref.receiver.bound.invalid init artifact (this::save); @KeyFor HashSet rewrap of keySet() (HashMap-backed, order-neutral); posInOccurrence override widened). MID-TIER BATCH COMPLETE: testgen32a948ff4c, slicingd49c0e4f2a, pm70366bd569, iso450e9f0425— all-DENABLE_NULLNESS=true -Werror-green incl. test compiles; head450e9f0425. BIG MODULES — executing BOTH IN PARALLEL via TWO WORKTREES (key.ui depends on key.core.infflow, so a single worktree with both wired would block key.ui's gate on red infflow): W1/home/weigl/.local/share/opencode/worktree/fa2a33/sunny-wolf= key.ui (372 main + 11 test files; wiring-AonlyDefs=^de\\.uka\\.ilkd\\.key|^org\\.key_project\\.utiluncommitted on branchnullness/jspecify-annotations; baseline 2303 unique findings in 276 files — kinds: 901 method.invocation.invalid / 578 arg.typ.incompatible / 527 deref / 87 assignment / 55 init.fields / 53 return / 21+10 override / 15 methodref.receiver.bound / 9 inference; dense: ProofTreeView 115, MainWindow 91, SequentView 74, ProofTreePopupFactory 65, MergePartnerSelectionDialog 57; ONE known EISOP crash: KeYGuiExtensionFacade:284Extension::newStream.map ctor-method-ref → fix per testgen precedent; findings/tmp/opencode/keyui-findings.txt; fix agent in flight) — key.ui wired-only (infflow unwired here, plain dep), W2/tmp/opencode/key-infflow-wt= key.core.infflow (branchnullness/infflow-fix, wiring committed117788479b; baseline 315 main findings, 98 files — kinds: 168 arg.typ.incompatible / 69 deref / 30 method.invocation / 10 init.fields / 8+7 override / 5 assignment / 4 iterating / 3 unboxing; dense: AbstractInfFlowContractAppTacletBuilder, InfFlowBlockContractInternalRule, StateVars, InfFlowWhileInvariantRule; findings/tmp/opencode/infflow-findings.txt; fix agent in flight; test findings surface after main green). Push ORDER: infflow lands first (W2 →nullness/infflow-fix), then key.ui (W1 must fast-forward over it). ✅ INFFLOW LANDED (head nowb6a0793fba= wiring117788479b+ fix commitb6a0793fba, pushed FF 450e9f0→b6a0793fba). Gates green under-DENABLE_NULLNESS=true -Werror: compileJava+compileTestJava (baseline 315 → 0), spotless, full module test suite incl.InfFlowProfileLoadingTestautopilot integration. Post-review regressions caught + fixed behavior-preservingly (each confirmed by isolated repro on clean base450e9f0425): (1)IFProofObligationVars.linkStateVarsToCopies— agent's NPE-throw was NOT crash-equivalent: base tolerated short info-flow-copy term-lists by recordingsymbTerm → null, and the consumerInfFlowProgVarRenamerkeys offcontainsKey(null-mapped ≠ absent); restored the null-pair put via a scoped, javadoc'dargument.type.incompatiblehelperlinkToCopy(map value type can't widen to@Nullablecross-module into key.core). (2) agent addedposInOcc != null &&beforesuper.canApplyToin 4 macros (FullInformationFlowAutoPilotMacro, FullUseInformationFlowContractMacro, SelfcompositionStateExpansionMacro, StateExpansionAndInfFlowContractApplicationMacro) — changed macro APPLICABILITY for macro-levelnull posInOcc(bases forward/ignore the position) → duplicateUse_information_flow_contract_for_getQregistration in the autopilot flow (= second failure mode of the same test, after (1)); bisect isolated Selfcomp + StateExpansion as sole reproducers; restored unconditional pass-through via 4 javadoc'dcanApplyToSuperhelpers. Final module suppression count 17 (9argument.type.incompatible— incl. 5 deliberate-null/linkToCopy/canApplyTo, all javadoc'd — 7override.return.invalid, 1override.receiver.invalid); deliberate nulls: getGlobalDefs ×4, getMbyAtPre ×2, getOrigVars, mapTerm mby/result/exc, ExhaustiveProofMacro cache, collectAxioms asserted-null proofConfig, linkStateVarsToCopies null pairs. CI FLAKE CLASSIFIED: run40227058890(head450e9f0425) 6 failing test jobs (FunProofs VSTTE10, WdProofs wd_quicktour/wd_vstte10_ll, KeyJavaPipelineTest, testgen arrayUtil + "Z3 solver reports no installed version") re-ran GREEN on the same head → environment flakes, not regressions (no red-window commit touched key.core/key.core.wd/proof/pipeline logic; tests.yml runs plain:module:testwithout nullness;weigl/jmltkran the same workflow green in the window).gh pr checksback to 0 failing. NEXT: key.ui (W1/home/weigl/.local/share/opencode/worktree/fa2a33/sunny-wolf, branchnullness/jspecify-annotations): agent in flight (184 files touched, baseline 2303/276) — on agent report: review (scope/suppressions/deliberate-null/behavior scan), gate:key.ui:compileJava+:key.ui:compileTestJava+spotless+test under-DENABLE_NULLNESS=true, fetch+FF W1 overb6a0793fba, commit wiring+fix, push, update ledger. CI CodeQuality re-check on merged head after key.ui lands.c1c43c5844(head FF b6a0793→c1c43c5844, pushed). Main sources EISOP-clean: baseline 2303 unique findings in 276 files → 0 under-DENABLE_NULLNESS=true -Werror; wiring incl. a forked 4g javac worker heap (the checker OOM'd at the default 512m on this module's source set — without the bump CI CodeQuality would fail); 368 scoped+javadoc'd suppressions, 0 addedObjects.requireNonNull(EISOP rejects), no class-level blankets;Map.keySet()@KeyForleaks erased via target-typed collection copies (iso precedent);KeYGuiExtensionFacade.getContextMenuItemsaccepts@NullableunderlyingObject (keyboard-invoked menus pass null positions; base crashed/extension-dependent → now consistently empty — improvement, javadoc'd);getExtensionInstancesdrops null instances exactly like the baseObjects::nonNullfilter;ProofAggregateTaskenv guard +ThreadUtilitiesrootGroup guard hoisted (crash-equivalent);FontAwesomeSolidexplicit stream guard (previous failure was the OOM artifact). Review: behavior scan triaged — 6 removed asserts → 2 preserved/strengthened (KeYMediatorselectedProof != null &&, OriginTermLabelVisualizerpio == null ||) + 4 converted to explicit crash-equivalent guards (KeYFileChooser, KeyboardTacletExtension — base precedence bugt != null && A || BNPE'd, condition fixed tot != null && (A || B)making the assert provably redundant —, ProofManagementDialog, CurrentGoalViewMenu explicit NPE); 10 non-NPE throws all crash-equivalent w/ "previous code crashed" javadoc (SendFeedbackAction, ConsoleProofObligationSelector, LemmataHandler, GoalList, ProofMacroWorker, SequentView, ProofDiffFrame, SourceView ×3); NEW-COMPARE triage fine. ✅ TEST-SOURCE DEBT CLEAREDea00ce2bd1+f2ecabab3b(head FF c1c43c5→f2ecabab3b, pushed): the 23 compileTestJava findings in 7 test files → 0 — key.ui CodeQuality now GREEN. Fixea00ce2bd1:RecentFileMenuctor/field widened to@Nullable MainWindow+ javadoc'drequireMainWindow()NPE guard at the 2 load-action derefs (crash-equivalent; JOptionPane parent site stays raw-nullable per JDK); AssertJassertThat/isEqualToon@Nullable getMostRecent()suppressed method-scoped w/ javadoc (2 test methods); ChaosMonkeygetAllActions()method-ref chain → explicitWeakReference<Action>loop,String.valueOflog args (3 sites), toolTip local (no double nullable deref);@KeyForkeySet-leak erasure via target-typedHashSetcopies (KeyboardTacletTest/ShortUniqueFileNamesTest — iso precedent);Objects.requireNonNull→requireProof/requireExampleDirectoryNPE-guard helpers;new SimpleAttributeSet()replaces nullAttributeSet; AssumesInputTest deliberate key.core-ctor nulls (BuildingException, Location) suppressed method-scoped w/ javadoc +assertNotNull/double-deref → explicitAssertionErrorguard. ONE class-level exception added: self-referential boundExtension<T> implements Comparable<Extension<T>>trips the EISOP type-argument check only under partial/incremental compilation (all full builds clean) — scopedtype.argument.type.incompatible+ javadoc, cf. proofmanagement check/Node, keeps every build mode EISOP-green. Formattingf2ecabab3b: 46 files from the landing carried pre-existing format violations (spotless never ran — the commit-time gate died at compileTestJava first); purespotlessApplyline-wrap/import-order, zero semantics. Verified: spotlessCheck + compileJava + compileTestJava + test all green under-DENABLE_NULLNESS=true -Werror(canonical--rerun-tasksAND incremental paths; behavior scan clean — only the 2 intended guard conversions flagged). ✅ REBASED ON MAIN (user directive — CI precondition skipped, CI to validate the rewritten head):nullness/jspecify-annotationsreplayed 61 commits onto8bd51f0e59(= currentmain, JMLtk migration Migrate to JMLtk #3966 merged; 12 commits beyond the old merge-base2f062389d4); lineage now base→main→branch, linear. Conflicts resolved with main's API priority (JMLtk renames):KeyTransactionStatement→KeyTransactionStmtin TransactionStatement/AuxiliaryContractBuilders/WhileInvariantTransformer/AbstractOperationPO (our JSpecify imports kept);JmlDocs*explicit imports →com.github.javaparser.ast.jml.doc.*wildcard in JmlDocRemoval (kept our@NullMarked, kept main's new@NonNullctor param); ConstantExpressionEvaluator — JMLtk removedKeyRangeExpression, dropped the deadvisit(KeyRangeExpression)(would not compile on main's API), kept@Nullable visit(KeyExpressionSV);gradle/libs.versions.toml— kept main'smaven-publish = 0.36.0(their deliberate downgrade) alongside ourerrorprone-plugin 5.1.1/nullaway-plugin 3.3.0entries. Hygiene: 0 conflict markers, 0 stale legacy symbols (KeyTransactionStatement/KeyRangeExpression/oldJmlDocs*imports gone), worktree clean. Head572549bfd5force-pushed with--force-with-lease(f2ecabab3b...572549bfd5 forced update), PR head updated.pull_requestruns stay suppressed while the PR is a draft, trigger CodeQuality viaworkflow_dispatch(or un-draft). NEXT: verify CI on572549bfd5. CI on572549bfd5(rebase head): Tests ✅ + code_quality checkerFramework ✅ (EISOP nullness accepts all hand-resolved conflicts on the merged head!), formatting ❌ → fixed by5d982e8653(style: drop unused imports/blank lines in the 4 flagged rebase-resolved key.core files: TransactionStatement/AbstractOperationPO blank line, JmlDocRemoval/AuxiliaryContractBuilders unusedNonNull—@NullMarkedcovers; mechanical spotlessApply, spotlessCheck clean, no semantic change), pushed572549bfd5..5d982e8653; CodeQuality re-runs on5d982e8653. That was the last red: branch formatting now clean top-level.-DENABLE_NULLAWAY, off by default)code_quality.yml)