Skip to content

Commit 995c2d3

Browse files
timtebeekclaude
andauthored
Fix ReplaceLambdaWithMethodReference for local records and classes (#336) (#777)
The recipe was generating malformed method references like `1R::s` instead of `R::s` when converting lambdas that reference locally-defined records or classes. This occurred because local classes in Java bytecode are named with patterns like `OuterClass$1LocalName`, where the digit indicates the method scope. The fix modifies `JavaElementFactory.className()` to detect local classes (by checking for `$<digit>` patterns) and extract just the simple class name without the numeric prefix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0ea5975 commit 995c2d3

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,20 @@ static Expression className(JavaType type, boolean qualified) {
8080
qualifiedName = type.toString();
8181
}
8282

83+
// Check if this is a local class (contains $<digit>)
84+
// Local classes have names like "Outer$1LocalName" where the digit indicates the scope
85+
// For method references, we should use only the simple name without the numeric prefix
86+
if (qualifiedName.matches(".*\\$\\d+.*")) {
87+
// Extract the simple name after the last $<digit> sequence
88+
String simpleName = qualifiedName.replaceAll(".*\\$\\d+", "");
89+
// Remove any remaining $ characters and get just the class name
90+
simpleName = simpleName.replace('$', '.');
91+
if (simpleName.indexOf('.') > 0) {
92+
simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1);
93+
}
94+
return new J.Identifier(randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), simpleName, type, null);
95+
}
96+
8397
Scanner scanner = new Scanner(qualifiedName.replace('$', '.')).useDelimiter("\\.");
8498
for (int i = 0; scanner.hasNext(); i++) {
8599
String part = scanner.next();

src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,4 +1636,154 @@ void bar() {
16361636
)
16371637
);
16381638
}
1639+
1640+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/336")
1641+
@Test
1642+
void localRecordMethodReference() {
1643+
rewriteRun(
1644+
//language=java
1645+
java(
1646+
"""
1647+
import java.util.stream.Stream;
1648+
1649+
class Test {
1650+
String method() {
1651+
record R(String s) {}
1652+
return Stream.of(new R("hello world"))
1653+
.map(r -> r.s())
1654+
.findFirst()
1655+
.orElse(null);
1656+
}
1657+
}
1658+
""",
1659+
"""
1660+
import java.util.stream.Stream;
1661+
1662+
class Test {
1663+
String method() {
1664+
record R(String s) {}
1665+
return Stream.of(new R("hello world"))
1666+
.map(R::s)
1667+
.findFirst()
1668+
.orElse(null);
1669+
}
1670+
}
1671+
"""
1672+
)
1673+
);
1674+
}
1675+
1676+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/336")
1677+
@Test
1678+
void localClassMethodReference() {
1679+
rewriteRun(
1680+
//language=java
1681+
java(
1682+
"""
1683+
import java.util.stream.Stream;
1684+
1685+
class Test {
1686+
String method() {
1687+
class Local {
1688+
String getValue() { return "test"; }
1689+
}
1690+
return Stream.of(new Local())
1691+
.map(l -> l.getValue())
1692+
.findFirst()
1693+
.orElse(null);
1694+
}
1695+
}
1696+
""",
1697+
"""
1698+
import java.util.stream.Stream;
1699+
1700+
class Test {
1701+
String method() {
1702+
class Local {
1703+
String getValue() { return "test"; }
1704+
}
1705+
return Stream.of(new Local())
1706+
.map(Local::getValue)
1707+
.findFirst()
1708+
.orElse(null);
1709+
}
1710+
}
1711+
"""
1712+
)
1713+
);
1714+
}
1715+
1716+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/336")
1717+
@Test
1718+
void localRecordWithInstanceOfCheck() {
1719+
rewriteRun(
1720+
//language=java
1721+
java(
1722+
"""
1723+
import java.util.List;
1724+
import java.util.stream.Collectors;
1725+
1726+
class Test {
1727+
List<Object> method(List<Object> input) {
1728+
record R(String s) {}
1729+
return input.stream()
1730+
.filter(o -> o instanceof R)
1731+
.collect(Collectors.toList());
1732+
}
1733+
}
1734+
""",
1735+
"""
1736+
import java.util.List;
1737+
import java.util.stream.Collectors;
1738+
1739+
class Test {
1740+
List<Object> method(List<Object> input) {
1741+
record R(String s) {}
1742+
return input.stream()
1743+
.filter(R.class::isInstance)
1744+
.collect(Collectors.toList());
1745+
}
1746+
}
1747+
"""
1748+
)
1749+
);
1750+
}
1751+
1752+
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/336")
1753+
@Test
1754+
void localRecordWithCast() {
1755+
rewriteRun(
1756+
//language=java
1757+
java(
1758+
"""
1759+
import java.util.List;
1760+
import java.util.stream.Collectors;
1761+
1762+
class Test {
1763+
List<Object> method(List<Object> input) {
1764+
record R(String s) {}
1765+
return input.stream()
1766+
.filter(R.class::isInstance)
1767+
.map(o -> (R) o)
1768+
.collect(Collectors.toList());
1769+
}
1770+
}
1771+
""",
1772+
"""
1773+
import java.util.List;
1774+
import java.util.stream.Collectors;
1775+
1776+
class Test {
1777+
List<Object> method(List<Object> input) {
1778+
record R(String s) {}
1779+
return input.stream()
1780+
.filter(R.class::isInstance)
1781+
.map(R.class::cast)
1782+
.collect(Collectors.toList());
1783+
}
1784+
}
1785+
"""
1786+
)
1787+
);
1788+
}
16391789
}

0 commit comments

Comments
 (0)