Skip to content

Commit 1251368

Browse files
committed
Skip UseMapOf rewrite when the assignment target is a concrete HashMap (#1148)
1 parent ceb4af5 commit 1251368

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

src/main/java/org/openrewrite/java/migrate/util/UseMapOf.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
111111
TypeUtils.isOfClassType(n.getClazz() != null ? n.getClazz().getType() : null, "java.util.HashMap")) {
112112
Statement statement = body.getStatements().get(0);
113113
if (statement instanceof J.Block) {
114+
// Skip when the result is assigned to a concrete `HashMap` declared type rather
115+
// than `Map`: the immutable `Map.of(..)` is not assignable to `HashMap`, and
116+
// `HashMap`-only methods may be invoked on the variable later (issue #1148).
117+
if (isAssignedToConcreteHashMap()) {
118+
return n;
119+
}
114120
List<Statement> putStatements = ((J.Block) statement).getStatements();
115121
List<Expression> args = new ArrayList<>();
116122
boolean useEntries = putStatements.size() > 10;
@@ -194,6 +200,19 @@ private J reattachPairPrefixes(J applied, List<J.MethodInvocation> puts, boolean
194200
return nc.withArguments(Collections.singletonList(mapCall.withArguments(withPrefixes)));
195201
}
196202

203+
/**
204+
* Returns {@code true} when the {@code new HashMap<>() {{ ... }}} currently being visited is
205+
* the initializer of a variable whose declared type is the concrete {@code java.util.HashMap}
206+
* rather than the {@code Map} interface. In that case the immutable {@code Map.of(..)} result
207+
* would not be assignable, and {@code HashMap}-specific methods may be used later, so the
208+
* rewrite is skipped (issue #1148).
209+
*/
210+
private boolean isAssignedToConcreteHashMap() {
211+
Object parent = getCursor().getParentTreeCursor().getValue();
212+
return parent instanceof J.VariableDeclarations.NamedVariable &&
213+
TypeUtils.isOfClassType(((J.VariableDeclarations.NamedVariable) parent).getType(), "java.util.HashMap");
214+
}
215+
197216
@Override
198217
public J visitBlock(J.Block block, ExecutionContext ctx) {
199218
Map<UUID, List<J.MethodInvocation>> rewrites = new HashMap<>();

src/test/java/org/openrewrite/java/migrate/util/UseMapOfTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,48 @@ class Test {
205205
);
206206
}
207207

208+
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1148")
209+
@Test
210+
void doNotChangeConcreteHashMapField() {
211+
//language=java
212+
rewriteRun(
213+
spec -> spec.allSources(s -> s.markers(javaVersion(25))),
214+
java(
215+
"""
216+
import java.util.HashMap;
217+
218+
class Main {
219+
private static final HashMap<String, String> VALUES = new HashMap<String, String>() {{
220+
put("key", "value");
221+
}};
222+
}
223+
"""
224+
)
225+
);
226+
}
227+
228+
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1148")
229+
@Test
230+
void doNotChangeConcreteHashMapLocalVariable() {
231+
//language=java
232+
rewriteRun(
233+
java(
234+
"""
235+
import java.util.HashMap;
236+
237+
class Main {
238+
void m() {
239+
HashMap<String, Integer> ages = new HashMap<>() {{
240+
put("Bob", 42);
241+
put("alice", 30);
242+
}};
243+
}
244+
}
245+
"""
246+
)
247+
);
248+
}
249+
208250
@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/1112")
209251
@Test
210252
void doNotChangeLinkedHashMap() {

0 commit comments

Comments
 (0)