Skip to content

Skip UseMapOf when the anonymous HashMap is assigned to a concrete HashMap variable#1149

Merged
timtebeek merged 1 commit into
mainfrom
tim/usemapof-widen-hashmap-1148
Jun 25, 2026
Merged

Skip UseMapOf when the anonymous HashMap is assigned to a concrete HashMap variable#1149
timtebeek merged 1 commit into
mainfrom
tim/usemapof-widen-hashmap-1148

Conversation

@timtebeek

@timtebeek timtebeek commented Jun 25, 2026

Copy link
Copy Markdown
Member

Fixes #1148

Problem

For the anonymous-class form, UseMapOf replaced new HashMap<>() {{ put(..); }} with an immutable Map.of(..) but left the declared type untouched. When the variable was declared with the concrete type HashMap (rather than the Map interface), the result no longer compiled, since Map.of(..) returns an immutable java.util.Map that is not assignable to a HashMap variable:

private static final HashMap<String, String> VALUES = new HashMap<>() {{ put("key", "value"); }};
// became uncompilable:
private static final HashMap<String, String> VALUES = Map.of("key", "value");

Fix

Skip the anonymous-class rewrite when its result is assigned to a variable whose declared type is the concrete HashMap rather than the Map interface (option 2 in the issue). Beyond the assignability problem, widening the declared type to Map would also risk breaking later calls to HashMap-specific methods, so leaving such declarations untouched is the safer behaviour.

The guard checks the enclosing variable's declared type straight off the NamedVariable:

private boolean isAssignedToConcreteHashMap() {
    Object parent = getCursor().getParentTreeCursor().getValue();
    return parent instanceof J.VariableDeclarations.NamedVariable &&
            TypeUtils.isOfClassType(((J.VariableDeclarations.NamedVariable) parent).getType(), "java.util.HashMap");
}

The common cases — assigning to a Map-typed variable, returning, or using the value inline — are unaffected and still rewrite to Map.of(..). The mutable prose form (new HashMap<>(); x.put(..)new HashMap<>(Map.of(..))) already stays a HashMap and so was never affected.

Tests

Added field and local-variable cases asserting that a concrete HashMap declaration is left unchanged (the field case mirrors the issue's reproducer with javaVersion(25)). Existing tests — including the Map-interface, LinkedHashMap/TreeMap, null-bail, and prose-form cases — continue to pass.

@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Jun 25, 2026
@timtebeek timtebeek force-pushed the tim/usemapof-widen-hashmap-1148 branch from b80a19b to cb3243d Compare June 25, 2026 11:32
@timtebeek timtebeek changed the title Widen concrete HashMap declared type when UseMapOf rewrites it to Map.of(..) Skip UseMapOf rewrite when the assignment target is a concrete HashMap Jun 25, 2026
@timtebeek timtebeek force-pushed the tim/usemapof-widen-hashmap-1148 branch from cb3243d to 1251368 Compare June 25, 2026 11:43
@timtebeek timtebeek changed the title Skip UseMapOf rewrite when the assignment target is a concrete HashMap Skip UseMapOf when the anonymous HashMap is assigned to a concrete HashMap variable Jun 25, 2026
@timtebeek timtebeek merged commit b6c9ae6 into main Jun 25, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Jun 25, 2026
@timtebeek timtebeek deleted the tim/usemapof-widen-hashmap-1148 branch June 25, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

UseMapOf rewrites the initializer of a HashMap-typed variable to Map.of(), leaving uncompilable code

1 participant