Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Groovy parser does not support multiple closure arguments without parentheses #4772

Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,7 @@ public void visitArgumentlistExpression(ArgumentListExpression expression) {
args.add(JRightPadded.build((Expression) new J.Empty(randomId(), whitespace(), Markers.EMPTY))
.withAfter(omitParentheses == null ? sourceBefore(")") : EMPTY));
} else {
boolean lastArgumentsAreAllClosures = endsWithClosures(expression.getExpressions());
for (int i = 0; i < unparsedArgs.size(); i++) {
org.codehaus.groovy.ast.expr.Expression rawArg = unparsedArgs.get(i);
Expression arg = visit(rawArg);
Expand All @@ -867,7 +868,7 @@ public void visitArgumentlistExpression(ArgumentListExpression expression) {
if (omitParentheses == null) {
after = sourceBefore(")");
}
} else {
} else if (!(arg instanceof J.Lambda) || !(lastArgumentsAreAllClosures && omitParentheses != null)) {
jevanlingen marked this conversation as resolved.
Show resolved Hide resolved
after = whitespace();
if (source.charAt(cursor) == ')') {
// the next argument will have an OmitParentheses marker
Expand All @@ -883,6 +884,22 @@ public void visitArgumentlistExpression(ArgumentListExpression expression) {
queue.add(JContainer.build(beforeOpenParen, args, Markers.EMPTY));
}

public boolean endsWithClosures(List<org.codehaus.groovy.ast.expr.Expression> list) {
boolean foundNonClosure = false;

for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i) instanceof ClosureExpression) {
if (foundNonClosure) {
return false;
}
} else {
foundNonClosure = true;
}
}

return list.get(list.size() - 1) instanceof ClosureExpression;
}
jevanlingen marked this conversation as resolved.
Show resolved Hide resolved

@Override
public void visitClassExpression(ClassExpression clazz) {
String unresolvedName = clazz.getType().getUnresolvedName().replace('$', '.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,16 +295,16 @@ public J visitLambda(J.Lambda lambda, PrintOutputCapture<P> p) {
LambdaStyle ls = lambda.getMarkers().findFirst(LambdaStyle.class)
.orElse(new LambdaStyle(null, false, !lambda.getParameters().getParameters().isEmpty()));
boolean parenthesized = lambda.getParameters().isParenthesized();
if(!ls.isJavaStyle()) {
if (!ls.isJavaStyle()) {
p.append('{');
}
visitMarkers(lambda.getParameters().getMarkers(), p);
visitSpace(lambda.getParameters().getPrefix(), Space.Location.LAMBDA_PARAMETERS_PREFIX, p);
if(parenthesized) {
if (parenthesized) {
p.append('(');
}
visitRightPadded(lambda.getParameters().getPadding().getParameters(), JRightPadded.Location.LAMBDA_PARAM, ",", p);
if(parenthesized) {
if (parenthesized) {
p.append(')');
}
if (ls.isArrow()) {
Expand All @@ -318,7 +318,7 @@ public J visitLambda(J.Lambda lambda, PrintOutputCapture<P> p) {
} else {
visit(lambda.getBody(), p);
}
if(!ls.isJavaStyle()) {
if (!ls.isJavaStyle()) {
p.append('}');
}
afterSyntax(lambda, p);
Expand Down Expand Up @@ -358,6 +358,7 @@ public J visitForEachLoop(J.ForEachLoop forEachLoop, PrintOutputCapture<P> p) {
afterSyntax(forEachLoop, p);
return forEachLoop;
}

@Override
public J visitMethodDeclaration(J.MethodDeclaration method, PrintOutputCapture<P> p) {
beforeSyntax(method, Space.Location.METHOD_DECLARATION_PREFIX, p);
Expand Down Expand Up @@ -417,29 +418,32 @@ public J visitMethodInvocation(J.MethodInvocation method, PrintOutputCapture<P>

visitSpace(argContainer.getBefore(), Space.Location.METHOD_INVOCATION_ARGUMENTS, p);
List<JRightPadded<Expression>> args = argContainer.getPadding().getElements();
boolean argsAreAllClosures = args.stream().allMatch(it -> it.getElement() instanceof J.Lambda);
boolean omitParentheses = false;
boolean applyTrailingLambdaParenthese = true;
for (int i = 0; i < args.size(); i++) {
JRightPadded<Expression> arg = args.get(i);
boolean omitParens = arg.getElement().getMarkers()
.findFirst(OmitParentheses.class)
.isPresent() ||
arg.getElement().getMarkers()
.findFirst(org.openrewrite.java.marker.OmitParentheses.class)
.isPresent();

if (i == 0 && !omitParens) {
p.append('(');
} else if (i > 0 && omitParens && (
!args.get(0).getElement().getMarkers().findFirst(OmitParentheses.class).isPresent() &&
!args.get(0).getElement().getMarkers().findFirst(org.openrewrite.java.marker.OmitParentheses.class).isPresent()
)) {
p.append(')');
} else if (i > 0) {
boolean omitParensCurrElem = arg.getElement().getMarkers().findFirst(OmitParentheses.class).isPresent() ||
arg.getElement().getMarkers().findFirst(org.openrewrite.java.marker.OmitParentheses.class).isPresent();

if (i == 0) {
if (omitParensCurrElem) {
omitParentheses = true;
} else {
p.append('(');
}
} else if (!omitParentheses && omitParensCurrElem) { // first trailing lambda, eg: `stage('Build..') {}`, should close the method
if (applyTrailingLambdaParenthese) { // apply once, to support multiple closures: `foo("baz") {} {}
p.append(')');
applyTrailingLambdaParenthese = false;
}
} else if (!(omitParentheses && argsAreAllClosures)) {
p.append(',');
}

visitRightPadded(arg, JRightPadded.Location.METHOD_INVOCATION_ARGUMENT, p);

if (i == args.size() - 1 && !omitParens) {
if (i == args.size() - 1 && !omitParensCurrElem) {
p.append(')');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ void gradle() {
repositories {
mavenCentral()
}

copySpec {
from { 'src/main/webapp' } { exclude "**/*.jpg" }
rename '(.+)-staging(.+)', '$1$2'
}
jevanlingen marked this conversation as resolved.
Show resolved Hide resolved

dependencies {
implementation 'org.hibernate:hibernate-core:3.6.7.Final'
Expand Down Expand Up @@ -69,6 +74,19 @@ void emptyArgsWithParens() {
);
}

@Test
void noParentheses() {
rewriteRun(
groovy(
"""
class SomeObject {}
def foo(String a, int b, SomeObject c, String d) {}
foo "a", 3, new SomeObject(), "d"
"""
)
);
}

@Test
@SuppressWarnings("GroovyVariableNotAssigned")
void nullSafeDereference() {
Expand Down Expand Up @@ -180,6 +198,61 @@ def acceptsClosure(Closure cl) {}
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/4766")
@Test
void multipleClosureArgumentsWithoutParentheses() {
rewriteRun(
groovy(
"""
def foo(Closure a, Closure b, Closure c) {}
foo { } { } {
}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/4766")
@Test
void multipleClosureArgumentsWithParentheses() {
rewriteRun(
groovy(
"""
def foo(Closure a, Closure b, Closure c) {}
foo({ }, { }, {
})
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/4766")
@Test
void multipleArgumentsWithClosuresAndNonClosuresWithoutParentheses() {
rewriteRun(
groovy(
"""
def foo(String a, Closure b, Closure c, String d) {}
foo "a", { }, {
}, "d"
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/4766")
@Test
void trailingClosures() {
rewriteRun(
groovy(
"""
def foo(String a, int b, String c, Closure d, Closure e, Closure f) {}
foo("bar", 3, "baz") { } { } {}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/1236")
@Test
@SuppressWarnings("GroovyAssignabilityCheck")
Expand Down
Loading