Skip to content

Commit 8174845

Browse files
authored
Go: override Java receiver visitors for Go-specific type mismatches (#7258)
Go's AST uses nodes in positions where Java's model requires stricter types. For example, Go pointer types are J.Unary (not TypeTree), function literals are J.MethodDeclaration (not Expression), and Go primitives may arrive as JavaType.Class (not JavaType.Primitive). Add GolangReceiverDelegate overrides for visitArrayType, visitLiteral, visitMethodDeclaration, visitReturn, and visitVariableDeclarations. Each reads the RPC value without the Java-side type constraint, then uses the typed setter when possible or reflection when the Go node doesn't satisfy the Java interface. Tested against 15 popular Go repos (1,235 .go files total): 0 Go parse errors (100% success rate).
1 parent 88e8b50 commit 8174845

1 file changed

Lines changed: 139 additions & 0 deletions

File tree

rewrite-go/src/main/java/org/openrewrite/golang/internal/rpc/GolangReceiver.java

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,145 @@ public J visitForEachControl(J.ForEachLoop.Control control, RpcReceiveQueue q) {
286286
.getPadding().withIterable(JRightPadded.build(iterable));
287287
}
288288

289+
/**
290+
* Sets a final field to a value whose type doesn't match the declared field type.
291+
* Go's AST uses nodes (J.Unary for pointer types, J.MethodDeclaration for func
292+
* literals) in positions where Java's model requires TypeTree or Expression.
293+
*/
294+
private static void setFinalField(Object target, String fieldName, Object value) {
295+
try {
296+
java.lang.reflect.Field f = target.getClass().getDeclaredField(fieldName);
297+
f.setAccessible(true);
298+
f.set(target, value);
299+
} catch (Exception ignored) {
300+
}
301+
}
302+
303+
@SuppressWarnings({"unchecked", "rawtypes"})
304+
private J receiveAsJ(@Nullable Object before, RpcReceiveQueue q) {
305+
return (J) ((RpcReceiveQueue) q).receive(
306+
before,
307+
(java.util.function.UnaryOperator) t -> visitNonNull((J) t, q));
308+
}
309+
310+
@Override
311+
public J visitArrayType(J.ArrayType arrayType, RpcReceiveQueue q) {
312+
// Go slice types like []*T have pointer element types (J.Unary) that
313+
// don't implement TypeTree.
314+
J elementType = receiveAsJ(arrayType.getElementType(), q);
315+
if (elementType instanceof TypeTree) {
316+
arrayType = arrayType.withElementType((TypeTree) elementType);
317+
} else if (elementType != null) {
318+
setFinalField(arrayType, "elementType", elementType);
319+
}
320+
return arrayType
321+
.withAnnotations(q.receiveList(arrayType.getAnnotations(), a -> (J.Annotation) visitNonNull(a, q)))
322+
.withDimension(q.receive(arrayType.getDimension(), d -> visitLeftPadded(d, q)))
323+
.withType(q.receive(arrayType.getType(), t -> visitType(t, q)));
324+
}
325+
326+
@Override
327+
public J visitLiteral(J.Literal literal, RpcReceiveQueue q) {
328+
literal = literal
329+
.withValue(q.receive(literal.getValue()))
330+
.withValueSource(q.receive(literal.getValueSource()))
331+
.withUnicodeEscapes(q.receiveList(literal.getUnicodeEscapes(), s -> {
332+
int valueSourceIndex = q.receive(s != null ? s.getValueSourceIndex() : 0);
333+
String codePoint = q.receive(s != null ? s.getCodePoint() : null);
334+
return new J.Literal.UnicodeEscape(valueSourceIndex, codePoint);
335+
}));
336+
// Go may send JavaType.Class for int/string literals where Java expects JavaType.Primitive
337+
@SuppressWarnings({"unchecked", "rawtypes"})
338+
JavaType type = (JavaType) ((RpcReceiveQueue) q).receive(
339+
(Object) literal.getType(),
340+
(java.util.function.UnaryOperator) t -> visitType((JavaType) t, q));
341+
if (type instanceof JavaType.Primitive) {
342+
literal = literal.withType((JavaType.Primitive) type);
343+
} else if (type != null) {
344+
setFinalField(literal, "type", type);
345+
}
346+
return literal;
347+
}
348+
349+
@Override
350+
public J visitMethodDeclaration(J.MethodDeclaration method, RpcReceiveQueue q) {
351+
if (method.getAnnotations().getName() == null) {
352+
method = method.getAnnotations().withName(
353+
new J.MethodDeclaration.IdentifierWithAnnotations(null, null));
354+
}
355+
method = method
356+
.withLeadingAnnotations(q.receiveList(method.getLeadingAnnotations(),
357+
a -> (J.Annotation) visitNonNull(a, q)))
358+
.withModifiers(q.receiveList(method.getModifiers(),
359+
m -> (J.Modifier) visitNonNull(m, q)))
360+
.getPadding().withTypeParameters(q.receive(
361+
method.getPadding().getTypeParameters(),
362+
tp -> (J.TypeParameters) visitNonNull(tp, q)));
363+
364+
// Go return types can be pointer types (*T → J.Unary), slice types,
365+
// or Go-specific nodes that don't implement TypeTree.
366+
J returnType = receiveAsJ(method.getReturnTypeExpression(), q);
367+
if (returnType instanceof TypeTree) {
368+
method = method.withReturnTypeExpression((TypeTree) returnType);
369+
} else if (returnType != null) {
370+
setFinalField(method, "returnTypeExpression", returnType);
371+
}
372+
373+
return method
374+
.getAnnotations().withName(method.getAnnotations().getName()
375+
.withAnnotations(q.receiveList(
376+
method.getAnnotations().getName().getAnnotations(),
377+
a -> (J.Annotation) visitNonNull(a, q))))
378+
.withName(q.receive(method.getName(),
379+
n -> (J.Identifier) visitNonNull(n, q)))
380+
.getPadding().withParameters(q.receive(
381+
method.getPadding().getParameters(), p -> visitContainer(p, q)))
382+
.getPadding().withThrows(q.receive(
383+
method.getPadding().getThrows(), t -> visitContainer(t, q)))
384+
.withBody(q.receive(method.getBody(),
385+
b -> (J.Block) visitNonNull(b, q)))
386+
.getPadding().withDefaultValue(q.receive(
387+
method.getPadding().getDefaultValue(),
388+
d -> visitLeftPadded(d, q)))
389+
.withMethodType(q.receive(method.getMethodType(),
390+
t -> (JavaType.Method) visitType(t, q)));
391+
}
392+
393+
@Override
394+
public J visitReturn(J.Return retrn, RpcReceiveQueue q) {
395+
// Go function literals in return position are J.MethodDeclaration, not Expression
396+
J expr = receiveAsJ(retrn.getExpression(), q);
397+
if (expr instanceof Expression) {
398+
return retrn.withExpression((Expression) expr);
399+
} else if (expr != null) {
400+
setFinalField(retrn, "expression", expr);
401+
}
402+
return retrn;
403+
}
404+
405+
@Override
406+
public J visitVariableDeclarations(J.VariableDeclarations variableDecls, RpcReceiveQueue q) {
407+
variableDecls = variableDecls
408+
.withLeadingAnnotations(q.receiveList(variableDecls.getLeadingAnnotations(),
409+
a -> (J.Annotation) visitNonNull(a, q)))
410+
.withModifiers(q.receiveList(variableDecls.getModifiers(),
411+
m -> (J.Modifier) visitNonNull(m, q)));
412+
413+
// Go pointer/slice/map types don't always implement TypeTree
414+
J typeExpr = receiveAsJ(variableDecls.getTypeExpression(), q);
415+
if (typeExpr instanceof TypeTree) {
416+
variableDecls = variableDecls.withTypeExpression((TypeTree) typeExpr);
417+
} else if (typeExpr != null) {
418+
setFinalField(variableDecls, "typeExpression", typeExpr);
419+
}
420+
421+
variableDecls = variableDecls.withVarargs(
422+
q.receive(variableDecls.getVarargs(), v -> visitSpace(v, q)));
423+
return variableDecls.getPadding().withVariables(
424+
q.receiveList(variableDecls.getPadding().getVariables(),
425+
v -> visitRightPadded(v, q)));
426+
}
427+
289428
@Override
290429
public J visitImport(J.Import importStmt, RpcReceiveQueue q) {
291430
importStmt = importStmt.getPadding().withStatic(

0 commit comments

Comments
 (0)