diff --git a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java index 526329e7a..19535935d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java @@ -65,6 +65,8 @@ public Duration getEstimatedEffortPerOccurrence() { rspecRulesReplaceTypeMap.put("java.util.AbstractSequentialList", "java.util.List"); rspecRulesReplaceTypeMap.put("java.util.ArrayList", "java.util.List"); rspecRulesReplaceTypeMap.put("java.util.concurrent.CopyOnWriteArrayList", "java.util.List"); + rspecRulesReplaceTypeMap.put("java.util.LinkedList", "java.util.List"); + rspecRulesReplaceTypeMap.put("java.util.Stack", "java.util.List"); rspecRulesReplaceTypeMap.put("java.util.Vector", "java.util.List"); // Map rspecRulesReplaceTypeMap.put("java.util.AbstractMap", "java.util.Map"); @@ -73,6 +75,7 @@ public Duration getEstimatedEffortPerOccurrence() { rspecRulesReplaceTypeMap.put("java.util.Hashtable", "java.util.Map"); rspecRulesReplaceTypeMap.put("java.util.IdentityHashMap", "java.util.Map"); rspecRulesReplaceTypeMap.put("java.util.LinkedHashMap", "java.util.Map"); + rspecRulesReplaceTypeMap.put("java.util.TreeMap", "java.util.Map"); rspecRulesReplaceTypeMap.put("java.util.WeakHashMap", "java.util.Map"); // ConcurrentMap rspecRulesReplaceTypeMap.put("java.util.concurrent.ConcurrentHashMap", "java.util.concurrent.ConcurrentMap"); @@ -80,10 +83,12 @@ public Duration getEstimatedEffortPerOccurrence() { // Queue rspecRulesReplaceTypeMap.put("java.util.AbstractQueue", "java.util.Queue"); rspecRulesReplaceTypeMap.put("java.util.concurrent.ConcurrentLinkedQueue", "java.util.Queue"); + rspecRulesReplaceTypeMap.put("java.util.PriorityQueue", "java.util.Queue"); // Set rspecRulesReplaceTypeMap.put("java.util.AbstractSet", "java.util.Set"); rspecRulesReplaceTypeMap.put("java.util.HashSet", "java.util.Set"); rspecRulesReplaceTypeMap.put("java.util.LinkedHashSet", "java.util.Set"); + rspecRulesReplaceTypeMap.put("java.util.TreeSet", "java.util.Set"); rspecRulesReplaceTypeMap.put("java.util.concurrent.CopyOnWriteArraySet", "java.util.Set"); } @@ -279,6 +284,16 @@ private static class InterfaceIncompatibleMethodDetector extends JavaIsoVisitor< "addElement", "capacity", "copyInto", "elementAt", "elements", "ensureCapacity", "insertElementAt", "removeAllElements", "removeElement", "removeElementAt", "setElementAt", "setSize", "trimToSize")))); nonInterfaceMethods.put("java.util.ArrayList", unmodifiableSet(new HashSet<>(Arrays.asList( "ensureCapacity", "trimToSize")))); + nonInterfaceMethods.put("java.util.LinkedList", unmodifiableSet(new HashSet<>(Arrays.asList( + // These have been promoted to Java 21 SequencedCollection interface, but we don't check that here + "addFirst", "addLast", "getFirst", "getLast", "removeFirst", "removeLast", + "descendingIterator", "offerFirst", "offerLast", "peekFirst", "peekLast", "pollFirst", "pollLast", "pop", "push", "removeFirstOccurrence", "removeLastOccurrence")))); + nonInterfaceMethods.put("java.util.Stack", unmodifiableSet(new HashSet<>(Arrays.asList( + "empty", "peek", "pop", "push", "search")))); + nonInterfaceMethods.put("java.util.TreeMap", unmodifiableSet(new HashSet<>(Arrays.asList( + "ceilingEntry", "ceilingKey", "descendingKeySet", "descendingMap", "firstEntry", "firstKey", "floorEntry", "floorKey", "headMap", "higherEntry", "higherKey", "lastEntry", "lastKey", "lowerEntry", "lowerKey", "navigableKeySet", "pollFirstEntry", "pollLastEntry", "subMap", "tailMap")))); + nonInterfaceMethods.put("java.util.TreeSet", unmodifiableSet(new HashSet<>(Arrays.asList( + "ceiling", "descendingIterator", "descendingSet", "first", "floor", "headSet", "higher", "last", "lower", "pollFirst", "pollLast", "subSet", "tailSet")))); } @Override diff --git a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java index ba344da8b..ffdbdcd31 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java @@ -1222,7 +1222,7 @@ void annotatedReturnTypeRawArrayList() { """ import java.util.ArrayList; import org.jspecify.annotations.Nullable; - + class Test { public @Nullable ArrayList transform() { ArrayList res = new ArrayList(); @@ -1235,7 +1235,7 @@ class Test { import java.util.List; import org.jspecify.annotations.Nullable; - + class Test { public @Nullable List transform() { List res = new ArrayList(); @@ -1257,7 +1257,7 @@ void annotatedFieldTypeRawArrayList() { """ import java.util.ArrayList; import org.jspecify.annotations.Nullable; - + class Test { public @Nullable ArrayList values = new ArrayList(); } @@ -1267,7 +1267,7 @@ class Test { import java.util.List; import org.jspecify.annotations.Nullable; - + class Test { public @Nullable List values = new ArrayList(); } @@ -1349,4 +1349,323 @@ class Test { ) ); } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void linkedListToList() { + rewriteRun( + //language=java + java( + """ + import java.util.LinkedList; + + class Test { + public LinkedList method() { + return new LinkedList<>(); + } + } + """, + """ + import java.util.LinkedList; + import java.util.List; + + class Test { + public List method() { + return new LinkedList<>(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void stackToList() { + rewriteRun( + //language=java + java( + """ + import java.util.Stack; + + class Test { + public Stack values = new Stack<>(); + } + """, + """ + import java.util.List; + import java.util.Stack; + + class Test { + public List values = new Stack<>(); + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void treeMapToMap() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeMap; + + class Test { + private TreeMap createMap() { + return new TreeMap<>(); + } + } + """, + """ + import java.util.Map; + import java.util.TreeMap; + + class Test { + private Map createMap() { + return new TreeMap<>(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void treeSetToSet() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeSet; + + class Test { + TreeSet items = new TreeSet<>(); + } + """, + """ + import java.util.Set; + import java.util.TreeSet; + + class Test { + Set items = new TreeSet<>(); + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void priorityQueueToQueue() { + rewriteRun( + //language=java + java( + """ + import java.util.PriorityQueue; + + class Test { + public PriorityQueue getQueue() { + return new PriorityQueue<>(); + } + } + """, + """ + import java.util.PriorityQueue; + import java.util.Queue; + + class Test { + public Queue getQueue() { + return new PriorityQueue<>(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void doNotChangeLinkedListWhenUsingDequeMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.LinkedList; + + class Test { + public LinkedList list = new LinkedList<>(); + + void useDequeMethod() { + list.addFirst("first"); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void doNotChangeStackWhenUsingStackMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.Stack; + + class Test { + Stack createStack() { + Stack stack = new Stack<>(); + stack.push("item"); + return stack; + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void doNotChangeTreeMapWhenUsingNavigableMapMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeMap; + + class Test { + TreeMap map = new TreeMap<>(); + + String getFirstKey() { + return map.firstKey(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void doNotChangeTreeSetWhenUsingNavigableSetMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeSet; + + class Test { + public TreeSet set = new TreeSet<>(); + + public String first() { + return set.first(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void linkedListMethodInvocation() { + rewriteRun( + //language=java + java( + """ + import java.util.LinkedList; + + class Test { + public LinkedList list = new LinkedList<>(); + + public void useListMethod() { + list.add("item"); + } + } + """, + """ + import java.util.LinkedList; + import java.util.List; + + class Test { + public List list = new LinkedList<>(); + + public void useListMethod() { + list.add("item"); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void stackEmptyMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.Stack; + + class Test { + public Stack stack = new Stack<>(); + + boolean isEmpty() { + return stack.empty(); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void treeMapCeilingEntry() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeMap; + import java.util.Map; + + class Test { + TreeMap map = new TreeMap<>(); + + Map.Entry getCeiling(Integer key) { + return map.ceilingEntry(key); + } + } + """ + ) + ); + } + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/710") + @Test + void treeSetDescendingIterator() { + rewriteRun( + //language=java + java( + """ + import java.util.TreeSet; + import java.util.Iterator; + + class Test { + public TreeSet set = new TreeSet<>(); + + Iterator getDescending() { + return set.descendingIterator(); + } + } + """ + ) + ); + } }