fix: initialize fParticipants via reflection to prevent NPE in headless rename (#24) #25

Merged
fred merged 1 commit from fix/24-fparticipants-npe into main 2026-03-07 17:32:20 +00:00
Collaborator

Summary

  • Fixes NullPointerException in ProcessorBasedRefactoring.createChange() when renaming fields in headless mode
  • Root cause: checkAllConditions throws IllegalArgumentException (#22 fallback), so only checkInitialConditions runs — but fParticipants is only initialized in checkFinalConditions after the processor check that throws the IAE
  • Bytecode analysis of ProcessorBasedRefactoring.checkFinalConditions confirmed that calling it in a try-catch would not help (IAE is thrown before fParticipants assignment)
  • Fix: ensureParticipantsInitialized() sets fParticipants to Collections.emptyList() via reflection if still null before createChange()

Test plan

  • Rename a field with cross-module references (preview: true and preview: false)
  • Verify rename works for types/methods (non-field, normal path unaffected)
  • Check ~/.jdt-mcp/jdt-mcp.log for the reflection warning when field rename fallback is triggered

Closes #24

## Summary - Fixes `NullPointerException` in `ProcessorBasedRefactoring.createChange()` when renaming fields in headless mode - Root cause: `checkAllConditions` throws `IllegalArgumentException` (#22 fallback), so only `checkInitialConditions` runs — but `fParticipants` is only initialized in `checkFinalConditions` **after** the processor check that throws the IAE - Bytecode analysis of `ProcessorBasedRefactoring.checkFinalConditions` confirmed that calling it in a try-catch would **not** help (IAE is thrown before `fParticipants` assignment) - Fix: `ensureParticipantsInitialized()` sets `fParticipants` to `Collections.emptyList()` via reflection if still null before `createChange()` ## Test plan - [ ] Rename a field with cross-module references (`preview: true` and `preview: false`) - [ ] Verify rename works for types/methods (non-field, normal path unaffected) - [ ] Check `~/.jdt-mcp/jdt-mcp.log` for the reflection warning when field rename fallback is triggered Closes #24
fix: initialize fParticipants via reflection to prevent NPE in headless rename (#24)
All checks were successful
Build and Release / build (push) Successful in 4m0s
678a45e98b
In headless mode, checkAllConditions throws IllegalArgumentException
during field rename (ProjectScope issue #22). The fallback path only
calls checkInitialConditions, but ProcessorBasedRefactoring.createChange()
requires fParticipants to be initialized — which only happens in
checkFinalConditions after the processor check that throws the IAE.

Bytecode analysis of ProcessorBasedRefactoring.checkFinalConditions confirms
fParticipants is assigned (line 98+) AFTER processor.checkFinalConditions()
(line 48) where the IAE originates, so calling checkFinalConditions in a
try-catch would not help.

Fix: ensureParticipantsInitialized() uses reflection to set fParticipants
to Collections.emptyList() if still null before createChange() is called.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fred merged commit 678a45e98b into main 2026-03-07 17:32:20 +00:00
fred deleted branch fix/24-fparticipants-npe 2026-03-07 17:32:20 +00:00
Sign in to join this conversation.
No description provided.