Skip to content

Commit

Permalink
Support Groovy declared constructors (#4706)
Browse files Browse the repository at this point in the history
* Support Groovy declared constructors

Fixes: #4704

* Enable one more test
  • Loading branch information
knutwannheden authored Nov 23, 2024
1 parent dcc0ec6 commit d308e49
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,12 @@ class A {
}
sortedByPosition.computeIfAbsent(pos(field), i -> new ArrayList<>()).add(field);
}
for (ConstructorNode ctor : clazz.getDeclaredConstructors()) {
if (!appearsInSource(ctor)) {
continue;
}
sortedByPosition.computeIfAbsent(pos(ctor), i -> new ArrayList<>()).add(ctor);
}
Iterator<InnerClassNode> innerClassIterator = clazz.getInnerClasses();
while (innerClassIterator.hasNext()) {
InnerClassNode icn = innerClassIterator.next();
Expand All @@ -366,6 +372,8 @@ class A {
.map(ast -> {
if (ast instanceof FieldNode) {
visitField((FieldNode) ast);
} else if (ast instanceof ConstructorNode) {
visitConstructor((ConstructorNode) ast);
} else if (ast instanceof MethodNode) {
visitMethod((MethodNode) ast);
} else if (ast instanceof ClassNode) {
Expand Down Expand Up @@ -595,6 +603,109 @@ null, emptyList(),
));
}

@Override
public void visitConstructor(ConstructorNode ctor) {
Space fmt = whitespace();

List<J.Annotation> annotations = ctor.getAnnotations().stream()
.map(a -> {
visitAnnotation(a);
return (J.Annotation) pollQueue();
})
.collect(Collectors.toList());

List<J.Modifier> modifiers = visitModifiers(ctor.getModifiers());

// Constructor name might be in quotes
Space namePrefix = whitespace();
String ctorName;
if (source.startsWith(ctor.getDeclaringClass().getName(), cursor)) {
ctorName = ctor.getDeclaringClass().getName();
} else {
char openingQuote = source.charAt(cursor);
ctorName = openingQuote + ctor.getName() + openingQuote;
}
cursor += ctorName.length();
J.Identifier name = new J.Identifier(randomId(),
namePrefix,
Markers.EMPTY,
emptyList(),
ctorName,
null, null);

RewriteGroovyVisitor bodyVisitor = new RewriteGroovyVisitor(ctor, this);

// Parameter has no visit implementation, so we've got to do this by hand
Space beforeParen = sourceBefore("(");
List<JRightPadded<Statement>> params = new ArrayList<>(ctor.getParameters().length);
Parameter[] unparsedParams = ctor.getParameters();
for (int i = 0; i < unparsedParams.length; i++) {
Parameter param = unparsedParams[i];

List<J.Annotation> paramAnnotations = param.getAnnotations().stream()
.map(a -> {
visitAnnotation(a);
return (J.Annotation) pollQueue();
})
.collect(Collectors.toList());

TypeTree paramType;
if (param.isDynamicTyped()) {
paramType = new J.Identifier(randomId(), EMPTY, Markers.EMPTY, emptyList(), "", JavaType.ShallowClass.build("java.lang.Object"), null);
} else {
paramType = visitTypeTree(param.getOriginType());
}
JRightPadded<J.VariableDeclarations.NamedVariable> paramName = JRightPadded.build(
new J.VariableDeclarations.NamedVariable(randomId(), EMPTY, Markers.EMPTY,
new J.Identifier(randomId(), whitespace(), Markers.EMPTY, emptyList(), param.getName(), null, null),
emptyList(), null, null)
);
cursor += param.getName().length();

org.codehaus.groovy.ast.expr.Expression defaultValue = param.getInitialExpression();
if (defaultValue != null) {
paramName = paramName.withElement(paramName.getElement().getPadding()
.withInitializer(new JLeftPadded<>(
sourceBefore("="),
new RewriteGroovyVisitor(defaultValue, this).visit(defaultValue),
Markers.EMPTY)));
}
Space rightPad = sourceBefore(i == unparsedParams.length - 1 ? ")" : ",");

params.add(JRightPadded.build((Statement) new J.VariableDeclarations(randomId(), EMPTY,
Markers.EMPTY, paramAnnotations, emptyList(), paramType,
null, emptyList(),
singletonList(paramName))).withAfter(rightPad));
}

if (unparsedParams.length == 0) {
params.add(JRightPadded.build(new J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY)));
}

JContainer<NameTree> throws_ = ctor.getExceptions().length == 0 ? null : JContainer.build(
sourceBefore("throws"),
bodyVisitor.visitRightPadded(ctor.getExceptions(), null),
Markers.EMPTY
);

J.Block body = ctor.getCode() == null ? null :
bodyVisitor.visit(ctor.getCode());

queue.add(new J.MethodDeclaration(
randomId(), fmt, Markers.EMPTY,
annotations,
modifiers,
null,
null,
new J.MethodDeclaration.IdentifierWithAnnotations(name, emptyList()),
JContainer.build(beforeParen, params, Markers.EMPTY),
throws_,
body,
null,
typeMapping.methodType(ctor)
));
}

@SuppressWarnings({"ConstantConditions", "unchecked"})
private <T> T pollQueue() {
return (T) queue.poll();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.openrewrite.groovy.tree;

import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.openrewrite.Issue;
import org.openrewrite.groovy.GroovyParser;
Expand Down Expand Up @@ -125,7 +124,6 @@ void shouldNotFailWhenImportCannotBeResolved() {
}

@Issue("https://github.com/openrewrite/rewrite/issues/4704")
@Disabled
@Test
void addingToMaps() {
rewriteRun(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ void inParens() {
);
}

@Test
void declaration() {
rewriteRun(
groovy(
"""
class Pair {
String first
String second
Pair(String first, String second) {
this.first = first
this.second = second
}
}
"""
)
);
}

@Test
void anonymousClassDeclarationClosedOverVariable() {
rewriteRun(
Expand Down

0 comments on commit d308e49

Please sign in to comment.