Fix ChangeType inner class import handling when outer class import is present#7032
Fix ChangeType inner class import handling when outer class import is present#7032
Conversation
When renaming an inner class (e.g. foo.A\$Builder → bar.B\$Builder) where the source file imports the outer class (import foo.A), ChangeType now correctly: - Force-adds the new outer class import (import bar.B) when the old outer class import was removed, since the code accesses the inner class via qualified name (B.Builder) - Omits the redundant inner class import (import bar.B.Builder) in this case, since import bar.B already makes B.Builder accessible Also adds a test for renaming an inner class within the same outer class.
- Rename changeTypeOfInnerClassConstructor → changeTypeOfInnerClass (scenario: import foo.A.Builder only, rename to bar.A$Builder within same outer name) - Rename changeTypeOfInnerClass → changeTypeOfInnerCompletely (scenario: rename to a completely different outer class bar.B$Builder) - Rename renameInnerClassWithinSameOuterClass → changeTypeOfInnerClassImplicitly (scenario: outer class renamed, inner class implicitly follows via import) - Add @issue annotations linking tests to the GitHub issues they cover - Adjust test content to better represent the intent of each scenario
…er-class-import # Conflicts: # rewrite-java/src/main/java/org/openrewrite/java/ChangeType.java
| import foo.A; | ||
| import foo.A.Builder; |
There was a problem hiding this comment.
Why do we have both imports here in the test? I'd think we only need the first one right?
| """ | ||
| package bar; | ||
|
|
||
| public class B { | ||
| public static class Builder {} | ||
| } | ||
| """ |
There was a problem hiding this comment.
We shouldn't need this second depends on block right? As ChangeType synthesizes types for what it changes, and we only need to provide the correct types to compile the imports?
| void renameInnerClassWithinSameOuterClass() { | ||
| rewriteRun( | ||
| spec -> spec.recipe(new ChangeType("foo.A$Builder", "foo.A$Creator", null)) |
There was a problem hiding this comment.
Right now we're testing the fairly common case of A.B, but should we also test the variants for A.B.C ? I wonder if those are handled correctly for all the permutations of imported as A, A.B or A.B.C, and then changed as A, A$B or A$B$C.
| // Force-add the outer class import when the original outer class import was | ||
| // explicitly removed (meaning code used the outer class by name, e.g. "B" in "B.Builder"). | ||
| // When the outer class import was absent, the code uses only the simple inner class | ||
| // name (e.g. just "Y"), so AddImport can decide based on actual references. |
There was a problem hiding this comment.
In our comments we first use B, then switch to Y here; that seems needlessly confusing, or was that intentional?
Summary
foo.A$Builder→bar.B$Builder) where the source file imports the outer class (import foo.A),ChangeTypenow correctly force-addsimport bar.Band omits the redundantimport bar.B.Builder- sinceimport bar.Balready makesB.Builderaccessible.owningClassSamehelper to detect when only the package changes (not the outer class name), enablingAddImportto decide based on actual type references in that case.Test plan
changeTypeOfInnerClass- inner class renamed within same outer class name, different package: only outer class import addedchangeTypeOfInnerCompletely- inner class renamed to an entirely different outer class: only new outer class import added, no redundant inner importchangeTypeOfInnerClassImplicitly- outer class renamed with inner class following implicitly via import