Do not run ExtractExplicitConstructorInvocationArguments on non-Java sources - #1235
Merged
knutwannheden merged 1 commit intoSep 8, 2026
Conversation
…a sources The recipe's `JavaIsoVisitor` accepted any `JavaSourceFile`, so it also ran on Groovy compilation units. There it reached `JavaTemplate`, which generates its stub from the Groovy LST and parses it as Java, and `JavaTemplateParser.parseMethodArguments` threw on the result. Narrow the visitor to `J.CompilationUnit`. JEP 513 is a Java language feature, so Groovy and Kotlin sources are out of scope for this recipe.
knutwannheden
deleted the
extractexplicitconstructorinvocationarguments-cce-on-groovy
branch
September 8, 2026 01:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Running
ExtractExplicitConstructorInvocationArgumentsover a corpus of open-source repositories throws onspring-cloud/spring-cloud-contract, inspring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/SyntaxChecker.groovy:How a Groovy file gets here
The visitor is a
JavaIsoVisitor, whoseisAcceptabletakes anyJavaSourceFile, and that includesG.CompilationUnit. TheUsesJavaVersion<>(25)precondition does not narrow it either: theJavaVersionmarker on a Groovy source carries the module's Java version, so it matches.Groovy attributes a method type to
super(..)only under@CompileStatic, taken fromStaticTypesMarker.DIRECT_METHOD_CALL_TARGET.SyntaxChecker.groovyannotates its nested classes that way, so thegetMethodType() == nullbail-out does not catch them and the recipe goes on to build aJavaTemplate.Why the template stub comes back malformed
JavaTemplateParser.parseMethodArgumentsprints the call with its arguments stripped, then splices the new argument list in after the closing parenthesis:GroovyPrinter.visitMethodInvocationemits(and)only from inside its argument loop, so an empty argument list prints as a baresuper, thereplaceAllmatches nothing, and the arguments are dropped without a word. That is the whole difference between the two stubs:Object o = /*__TEMPLATE__*/super/*__TEMPLATE_STOP__*/;Object o = /*__TEMPLATE__*/super(uri, kind);/*__TEMPLATE_STOP__*/;A bare
superparses as aJ.FieldAccess, and the next line casts it toJ.MethodInvocation. With a one-argument call the templated region is empty instead and the same line throwsIndexOutOfBoundsExceptionoff.get(0).The printer is doing something
JavaPrinterdoes not —JavaPrinterdelegates tovisitContainer("(", …, ")"), which emits the delimiters whatever the container holds. It stays latent because a parsed Groovy LST never has an empty argument container:GroovyParserVisitorputs aJ.Emptythere forfoo(). Only a synthesizedwithArguments(emptyList())reaches it, which is exactly whatparseMethodArgumentsdoes. Worth fixing inrewrite-groovyon its own merits, and tracked separately.Fix
isAcceptablenow returnssourceFile instanceof J.CompilationUnit. JEP 513 is a Java language feature and the transformation is written as aJavaTemplate, so Groovy and Kotlin are out of scope for this recipe whatever the LST happens to allow. Repairing the printer would let the template through, not make this recipe right on a Groovy source, so the guard is the fix here either way.Nothing about the shape is specific to this recipe: any
JavaTemplate-based recipe reachingparseMethodArgumentswithout a source-file guard can hit the same line.Tests
doNotRunOnGroovySourcespins the guard with a minimal@CompileStaticGroovy source; without the fix it fails atJavaTemplateParser.java:223. TheSimpleJavaFileObjectsubclass fromSyntaxChecker.groovyreproduces the reported cast verbatim, but a two-linesuper(name.trim())defends the same guard, so that is what the suite keeps.Note for whoever reads CI: on JDK 25 this test class already fails 12 of 18 on
main, which is #1206. This change adds no failures and its own test passes.