Skip to content

Commit dcd341f

Browse files
authored
JavaScript type mapping (#6025)
1 parent adb3f45 commit dcd341f

89 files changed

Lines changed: 4131 additions & 853 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
{
22
"permissions": {
33
"allow": [
4+
"mcp__ide__getDiagnostics",
5+
"Bash(npm search:*)",
6+
"Bash(npm run:*)",
7+
"Bash(node:*)",
48
"Bash(npm view:*)",
59
"Bash(npx:*)",
610
"Bash(npm ls:*)",

rewrite-core/src/main/java/org/openrewrite/TreeVisitor.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,7 @@ public P reduce(Tree tree, P p, Cursor parent) {
224224
return defaultValue(null, p);
225225
}
226226

227-
boolean topLevel = false;
228-
if (visitCount == 0) {
229-
topLevel = true;
230-
}
227+
boolean topLevel = visitCount == 0;
231228

232229
visitCount++;
233230
setCursor(new Cursor(cursor, tree));
@@ -263,7 +260,7 @@ public P reduce(Tree tree, P p, Cursor parent) {
263260
if (topLevel) {
264261
if (t != null && afterVisit != null) {
265262
for (TreeVisitor<?, P> v : afterVisit) {
266-
v.setCursor(getCursor());
263+
v.setCursor(getCursor());
267264
//noinspection unchecked
268265
t = (T) v.visit(t, p);
269266
}

rewrite-core/src/main/java/org/openrewrite/rpc/RewriteRpc.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ public String print(SourceFile tree, Print.@Nullable MarkerPrinter markerPrinter
350350
return print(tree, new Cursor(null, Cursor.ROOT_VALUE), markerPrinter);
351351
}
352352

353+
public String print(Tree tree, Cursor parent) {
354+
return print(tree, parent, null);
355+
}
356+
353357
public String print(Tree tree, Cursor parent, Print.@Nullable MarkerPrinter markerPrinter) {
354358
localObjects.put(tree.getId().toString(), tree);
355359
return send("Print", new Print(tree.getId().toString(), getCursorIds(parent), markerPrinter), String.class);

rewrite-core/src/main/java/org/openrewrite/rpc/RpcReceiveQueue.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package org.openrewrite.rpc;
1717

18-
import com.github.benmanes.caffeine.cache.Caffeine;
19-
import com.github.benmanes.caffeine.cache.LoadingCache;
2018
import org.jspecify.annotations.Nullable;
2119
import org.objenesis.ObjenesisStd;
2220

@@ -29,16 +27,6 @@
2927

3028
public class RpcReceiveQueue {
3129
private static final ObjenesisStd objenesis = new ObjenesisStd();
32-
private static final LoadingCache<String, Object> instanceCache = Caffeine.newBuilder()
33-
.maximumSize(1_000)
34-
.build((String key) -> {
35-
try {
36-
Class<?> clazz = Class.forName(key);
37-
return objenesis.newInstance(clazz);
38-
} catch (ClassNotFoundException e) {
39-
throw new RuntimeException(e);
40-
}
41-
});
4230

4331
private final Deque<RpcObjectData> batch;
4432
private final Map<Integer, Object> refs;
@@ -124,6 +112,12 @@ public <T> T receive(@Nullable T before, @Nullable UnaryOperator<T> onChange) {
124112
before = message.getValueType() == null ?
125113
message.getValue() :
126114
newObj(message.getValueType());
115+
if (ref != null) {
116+
// For an object like JavaType that we will mutate in place rather than using
117+
// immutable updates because of its cyclic nature, the before instance will ultimately
118+
// be the same as the after instance below.
119+
refs.put(ref, before);
120+
}
127121
}
128122
// Intentional fall-through...
129123
case CHANGE:
@@ -175,8 +169,13 @@ public <T> T receive(@Nullable T before, @Nullable UnaryOperator<T> onChange) {
175169
}
176170

177171
private <T> T newObj(String type) {
178-
//noinspection unchecked
179-
return (T) requireNonNull(instanceCache.get(type));
172+
try {
173+
Class<?> clazz = Class.forName(type);
174+
//noinspection unchecked
175+
return (T) objenesis.newInstance(clazz);
176+
} catch (ClassNotFoundException e) {
177+
throw new RuntimeException(e);
178+
}
180179
}
181180

182181
/**

rewrite-core/src/main/java/org/openrewrite/rpc/RpcSendQueue.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,29 @@ public <T, U> void getAndSend(T parent, Function<T, @Nullable U> value, @Nullabl
6969
send(after, before, onChange == null || after == null ? null : () -> onChange.accept(after));
7070
}
7171

72+
public <T, U> void getAndSendListAsRef(@Nullable T parent,
73+
Function<T, @Nullable List<U>> values,
74+
Function<? super U, ?> id,
75+
@Nullable Consumer<U> onChange) {
76+
getAndSendList(parent, values, id, onChange, true);
77+
}
78+
7279
public <T, U> void getAndSendList(@Nullable T parent,
7380
Function<T, @Nullable List<U>> values,
74-
Function<U, ?> id,
81+
Function<? super U, ?> id,
7582
@Nullable Consumer<U> onChange) {
76-
List<U> after = values.apply(parent);
83+
getAndSendList(parent, values, id, onChange, false);
84+
}
85+
86+
private <T, U> void getAndSendList(@Nullable T parent,
87+
Function<T, @Nullable List<U>> values,
88+
Function<? super U, ?> id,
89+
@Nullable Consumer<U> onChange,
90+
boolean asRef) {
91+
List<U> after = parent == null ? null : values.apply(parent);
7792
//noinspection unchecked
7893
List<U> before = this.before == null ? null : values.apply((T) this.before);
79-
sendList(after, before, id, onChange);
94+
sendList(after, before, id, onChange, asRef);
8095
}
8196

8297
public <T> void send(@Nullable T after, @Nullable T before, @Nullable Runnable onChange) {
@@ -97,10 +112,11 @@ public <T> void send(@Nullable T after, @Nullable T before, @Nullable Runnable o
97112
}
98113
}
99114

100-
public <T> void sendList(@Nullable List<T> after,
101-
@Nullable List<T> before,
102-
Function<T, ?> id,
103-
@Nullable Consumer<T> onChange) {
115+
<T> void sendList(@Nullable List<T> after,
116+
@Nullable List<T> before,
117+
Function<? super T, ?> id,
118+
@Nullable Consumer<T> onChange,
119+
boolean asRef) {
104120
send(after, before, () -> {
105121
assert after != null : "A DELETE event should have been sent.";
106122

@@ -110,7 +126,7 @@ public <T> void sendList(@Nullable List<T> after,
110126
Integer beforePos = beforeIdx.get(id.apply(anAfter));
111127
Runnable onChangeRun = onChange == null ? null : () -> onChange.accept(anAfter);
112128
if (beforePos == null) {
113-
add(anAfter, onChangeRun);
129+
add(asRef ? Reference.asRef(anAfter) : anAfter, onChangeRun);
114130
} else {
115131
T aBefore = before == null ? null : before.get(beforePos);
116132
if (aBefore == anAfter) {
@@ -125,7 +141,7 @@ public <T> void sendList(@Nullable List<T> after,
125141
});
126142
}
127143

128-
private <T> Map<Object, Integer> putListPositions(List<T> after, @Nullable List<T> before, Function<T, ?> id) {
144+
private <T> Map<Object, Integer> putListPositions(List<T> after, @Nullable List<T> before, Function<? super T, ?> id) {
129145
Map<Object, Integer> beforeIdx = new IdentityHashMap<>();
130146
if (before != null) {
131147
for (int i = 0; i < before.size(); i++) {
@@ -144,7 +160,7 @@ private <T> Map<Object, Integer> putListPositions(List<T> after, @Nullable List<
144160
private void add(@Nullable Object after, @Nullable Runnable onChange) {
145161
Object afterVal = Reference.getValue(after);
146162
Integer ref = null;
147-
if (after instanceof Reference) {
163+
if (after instanceof Reference && afterVal != null) {
148164
if (refs.containsKey(afterVal)) {
149165
put(new RpcObjectData(ADD, null, null, refs.get(afterVal)));
150166
// No onChange call because the remote will be using an instance from its ref cache

rewrite-core/src/test/java/org/openrewrite/internal/StringUtilsTest.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,19 @@
1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.Issue;
2020

21+
import java.util.regex.Pattern;
22+
2123
import static org.assertj.core.api.Assertions.assertThat;
2224
import static org.openrewrite.internal.StringUtils.*;
2325

2426
class StringUtilsTest {
2527

28+
@Test
29+
void aspectJNameToPatternTest() {
30+
assertThat(Pattern.compile(aspectjNameToPattern("java.io..*")).matcher("java.io.File")).matches();
31+
assertThat(Pattern.compile(aspectjNameToPattern("@types/lodash..*")).matcher("@types/lodash.LoDashStatic")).matches();
32+
}
33+
2634
@Test
2735
void containsOnlyWhitespaceAndCommentsTest() {
2836
assertThat(containsOnlyWhitespaceAndComments("")).isTrue();
@@ -120,17 +128,17 @@ void globMatching() {
120128
void greatestCommonMargin() {
121129
assertThat(StringUtils.greatestCommonMargin(
122130
"""
123-
\s
124-
\s
131+
\s
125132
\s
126-
""")).isEqualTo(" ");
133+
\s
134+
""")).isEqualTo(" ");
127135

128136
assertThat(StringUtils.greatestCommonMargin(
129137
"""
130-
\s
131-
s\s
132-
\s
133-
""")).isEqualTo(" ");
138+
\s
139+
s\s
140+
\s
141+
""")).isEqualTo(" ");
134142

135143
assertThat(StringUtils.greatestCommonMargin("")).isEqualTo("");
136144
assertThat(StringUtils.greatestCommonMargin("\n\n")).isEqualTo("");

rewrite-core/src/test/java/org/openrewrite/rpc/RpcSendQueueTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ void sendList() throws Exception {
4646
latch.countDown();
4747
}, new IdentityHashMap<>());
4848

49-
q.sendList(after, before, Function.identity(), null);
49+
q.sendList(after, before, Function.identity(), null, false);
5050
q.flush();
5151

5252
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
@@ -68,7 +68,7 @@ void sendEnum() throws Exception {
6868
latch.countDown();
6969
}, new IdentityHashMap<>());
7070

71-
q.sendList(after, before, Function.identity(), null);
71+
q.sendList(after, before, Function.identity(), null, false);
7272
q.flush();
7373

7474
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
@@ -87,7 +87,7 @@ void emptyList() throws Exception {
8787
latch.countDown();
8888
}, new IdentityHashMap<>());
8989

90-
q.sendList(after, null, Function.identity(), null);
90+
q.sendList(after, null, Function.identity(), null, false);
9191
q.flush();
9292

9393
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();

rewrite-java-test/src/test/java/org/openrewrite/java/MethodMatcherTest.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ void invalidMethodMatcher() {
5757
.isValid()).isFalse();
5858
}
5959

60+
@Test
61+
void npmPackageNaming() {
62+
assertThat(MethodMatcher.validate("@types/lodash..* map(..)").isValid()).isTrue();
63+
assertTrue(typeRegex("@types/lodash..* map(..)").matcher("@types/lodash.LodashStatic").matches());
64+
}
65+
6066
@Test
6167
void anyTypeMatchesNullTargetType() {
6268
assertTrue(new MethodMatcher("*..* equals(Object)", true).matchesTargetType(null));
@@ -246,7 +252,7 @@ void matchesMemberReferenceAsExpressionUsage(String methodPattern) {
246252
"""
247253
package a;
248254
import java.util.function.Supplier;
249-
255+
250256
class A {
251257
Supplier<A> a = A::new;
252258
}
@@ -262,7 +268,7 @@ void matchesMethod() {
262268
java(
263269
"""
264270
package a;
265-
271+
266272
class A {
267273
void setInt(int value) {}
268274
int getInt() {}
@@ -288,12 +294,12 @@ void strictMatchMethodOverride() {
288294
java(
289295
"""
290296
package com.abc;
291-
297+
292298
class Parent {
293299
public void method(String s) {
294300
}
295301
}
296-
302+
297303
class Test extends Parent {
298304
@Override
299305
public void method(String s) {
@@ -316,7 +322,7 @@ void matchesMethodWithWildcardForClassInPackage() {
316322
java(
317323
"""
318324
package a;
319-
325+
320326
class A {
321327
void foo() {}
322328
}
@@ -360,7 +366,7 @@ void siteExample() {
360366
java(
361367
"""
362368
package com.yourorg;
363-
369+
364370
class Foo {
365371
void bar(int i, String s) {}
366372
void other() {
@@ -500,7 +506,7 @@ void arrayExample() {
500506
java(
501507
"""
502508
package com.yourorg;
503-
509+
504510
class Foo {
505511
void bar(String[] s) {}
506512
void test() {

rewrite-java/src/main/antlr/MethodSignatureLexer.g4

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ RPAREN : ')';
99
LBRACK : '[';
1010
RBRACK : ']';
1111
COMMA : ',';
12-
DOT : '.';
12+
13+
// The widening of DOT to include '/' allows matching of package separators
14+
// in JavaScript and TypeScript import paths, e.g. @types/lodash..* map(..)
15+
DOT : '.' | '/';
1316

1417
// §3.12 Operators
1518

@@ -32,6 +35,10 @@ Identifier
3235
fragment
3336
JavaLetter
3437
: [a-zA-Z$_] // these are the "java letters" below 0x7F
38+
|
39+
// The widening of JavaLetter to include '@' allows matching package name parts
40+
// in JavaScript and TypeScript import paths, e.g. @types/lodash..* map(..)
41+
'@'
3542
| // covers all characters above 0x7F which are not a surrogate
3643
~[\u0000-\u007F\uD800-\uDBFF]
3744
{Character.isJavaIdentifierStart(_input.LA(-1))}?

0 commit comments

Comments
 (0)