From c3649c58ee078478abc5ed37abe1d67966a95bc8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 24 Sep 2025 11:13:12 +0200 Subject: [PATCH 1/2] `ArrayIndexOutOfBoundsException` in `GroovyParserVisitor.determineParenthesisLevel` for Spock test --- .../groovy/tree/RealWorldGroovyTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/RealWorldGroovyTest.java b/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/RealWorldGroovyTest.java index 18e1a87901..a030582b8a 100644 --- a/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/RealWorldGroovyTest.java +++ b/rewrite-groovy/src/test/java/org/openrewrite/groovy/tree/RealWorldGroovyTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.openrewrite.Issue; +import org.openrewrite.groovy.GroovyParser; import org.openrewrite.test.RewriteTest; import static org.openrewrite.groovy.Assertions.groovy; @@ -251,6 +252,29 @@ void apply(Project project) { ); } + @Issue("https://github.com/openrewrite/rewrite/issues/6067") + @Test + void spockTestParsingIssue() { + // Test the specific Groovy parser issue with method names containing spaces + // and labels like "expect:" that was causing ArrayIndexOutOfBoundsException + rewriteRun( + spec -> spec.parser(GroovyParser.builder().classpath("spock-core")), + groovy( + """ + import spock.lang.Specification + + class GroovyTest extends Specification { + + def "formatting test"() { + expect: + 1 == 2 + } + } + """ + ) + ); + } + @Issue("https://github.com/spring-projects/spring-webflow/blob/v3.4.1/gradle/docs.gradle") @Test void springWebflowGradleDocs() { From 2ae0072132b2aa0eb01774d15354fabedee32db9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 24 Sep 2025 11:45:18 +0200 Subject: [PATCH 2/2] Initial parser improvements --- .../org/openrewrite/groovy/GroovyParserVisitor.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java index b68eafaa37..13a55ea95f 100644 --- a/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java +++ b/rewrite-groovy/src/main/java/org/openrewrite/groovy/GroovyParserVisitor.java @@ -202,11 +202,12 @@ public G.CompilationUnit visit(SourceUnit unit, ModuleNode ast) throws GroovyPar if (t instanceof StringIndexOutOfBoundsException) { throw new GroovyParsingException("Failed to parse " + sourcePath + ", cursor position likely inaccurate.", t); } + int safeCursor = Math.min(cursor, source.length()); throw new GroovyParsingException( "Failed to parse " + sourcePath + " at cursor position " + cursor + ". The surrounding characters in the original source are:\n" + - source.substring(Math.max(0, cursor - 250), cursor) + "~cursor~>" + - source.substring(cursor, Math.min(source.length(), cursor + 250)), t); + source.substring(Math.max(0, safeCursor - 250), safeCursor) + "~cursor~>" + + source.substring(safeCursor, Math.min(source.length(), safeCursor + 250)), t); } } @@ -222,7 +223,7 @@ public G.CompilationUnit visit(SourceUnit unit, ModuleNode ast) throws GroovyPar null, pkg, statements, - format(source, cursor, source.length()) + format(source, Math.min(cursor, source.length()), source.length()) ); } @@ -1850,7 +1851,9 @@ public void visitMethodCallExpression(MethodCallExpression call) { name = (J.Identifier) select.getElement(); select = null; } else { - throw new IllegalArgumentException("Unable to parse method call"); + // For special cases like Spock tests with method names containing spaces, + // we create an identifier with the method name even if we can't match it exactly in the source + name = new J.Identifier(randomId(), prefix, Markers.EMPTY, emptyList(), methodName != null ? methodName : "unknown", null, null); } }