fix(codegen): use ASTRewrite instead of string manipulation for member insertion #94
No reviewers
Labels
No labels
bug
build
enhancement
headless
P1-critical
P2-high
P3-medium
P4-low
refactoring
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ai-tools/jdt-mcp-server!94
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/52-codegen-astrewrite"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
CodeGenerationToolsinserted generated methods, constructors and getters/settersby searching
source.lastIndexOf('}')and splicing text at that offset. Thisbreaks for nested/inner classes and any content after the target type's real
closing brace: the naive search finds the LAST
}in the whole file, whichbelongs to the enclosing type rather than the (possibly nested) target type,
silently inserting the new member into the wrong class.
The triage for this issue recounted 8 occurrences of this pattern (not the
4 methods named in the issue text) — this PR migrates all 8:
addMethod,generateMissingMethods(interface stubs),generateGettersSetters,generateConstructor(two branches),generateEqualsHashCode,generateToString,generateDelegateMethods.Fix
Replace the string-splice with structural insertion via
ASTRewrite, followingthe pattern already established in this file's
ConvertToLambdaRefactoring/ImportOrganizer:ASTParser)AbstractTypeDeclarationby theIType's name-rangeoffset (works for nested/inner types, since the DOM tree mirrors nesting by
source position — no binding resolution needed)
ListRewrite(BODY_DECLARATIONS_PROPERTY)position — last, or after the lastfield declaration for constructors (preserving prior "insert after fields"
behavior)
getWorkingCopy/applyTextEdit/commitWorkingCopy)Out of scope
addImplementsClause()(used byjdt_implement_interface) still does its ownmanual string scanning for the
implementsclause. It wasn't part of the 8lastIndexOf('}')sites this issue's triage tracked (it's a different kind ofstring manipulation — extends/implements clause editing, not body-declaration
insertion), so it's intentionally untouched here.
README.md's "BekannteEinschränkungen" table already has a row for this (currently mislabeled as
jdt_extract_interface, which doesn't apply —jdt_extract_interfaceusesJDT's
ExtractInterfaceProcessorand never did string manipulation; the rowactually describes
addImplementsClause/jdt_implement_interface). Left as-issince deciding whether to keep, correct, or re-file that row is a judgement
call above this package's scope.
Test plan
jdt_add_methodon anested class (
Outer.Nested) landed the new method in the enclosingOuterclass instead ofNestedNestedjdt_generate_getters_setters,jdt_generate_constructor,jdt_generate_equals_hashcode,jdt_generate_tostringin sequenceagainst a fixture class — all landed at the correct position
(constructor after fields, others appended), resulting file
javac-compilesmvn -pl org.naturzukunft.jdt.mcp -am compilegreentests/smoke-test.shgreen (9/9)mvn clean package(once) to build the product used for the above testsCloses #52
🤖 Generated with Claude Code
https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL
CodeGenerationTools inserted generated methods, constructors and getters/setters by searching for source.lastIndexOf('}') and splicing text at that offset. This breaks for nested/inner classes and any content after the target type's real closing brace: the naive search finds the LAST '}' in the whole file, which belongs to the enclosing type rather than the (possibly nested) target type, silently inserting the new member into the wrong class. Replace all 8 occurrences of this pattern with structural insertion via ASTRewrite: parse the compilation unit into a DOM AST, locate the target type's AbstractTypeDeclaration by its IType name-range offset, and insert the generated source as a string placeholder into the correct ListRewrite(BODY_DECLARATIONS_PROPERTY) position (last, or after the last field declaration for constructors). Apply via the working-copy pattern already used by ConvertToLambdaRefactoring/ImportOrganizer in this file. Reproduced the bug first: jdt_add_method on a nested class landed the new method in the enclosing class instead, confirmed against the pre-fix binary. Same fixture verified correct placement after the fix, and that generated code still compiles. Note: addImplementsClause() (used by jdt_implement_interface) still does its own manual string scanning for the implements-clause and is intentionally out of scope here - it wasn't part of the 8 lastIndexOf('}') sites this issue tracked, and the parallel triage flagged it as a follow-up. Closes #52 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL