Support Kotlin sources in AssertThrowsOnLastStatement#1048
Conversation
|
@timtebeek , I attempted to revive apache/jmeter#6217 (which is mixed Java+Kotlin codebase), and it turns out several recipes crash on JMeter codebase. I might try update the other failing recipes in a similar way, however, I would like to know in advance if you are willing to accept those types of contributions. Looks like currently only one recipe supports Kotlin: https://github.com/search?q=repo%3Aopenrewrite%2Frewrite-testing-frameworks%20KotlinTemplate.builder&type=code while there are 100+ usages of The effective prompt was behind the lines of "reproduce the issue, support Kotlin, and add tests exploring weird corner-cases for the recipe" |
a265a58 to
6092202
Compare
|
@timtebeek , I see you removed several tests. Sure it is your call, however they were not duplicate. For example, Every other test lands in the So without this test, removing the special-casing (or the type-based fallback) silently names the extracted variable |
`AssertThrowsOnLastStatement` is a `JavaTemplate`-based recipe, and Java visitors also run over Kotlin because `K.CompilationUnit` implements `JavaSourceFile`. On a Kotlin file the recipe emitted Java syntax for the extracted variable declaration, producing invalid code such as `baz: String = baz()`, or crashing with "generated 0 statements". This surfaced in mixed Java/Kotlin projects, where the recipe runs as part of the `junit-jupiter` migration. The statement-hoisting logic already operates on shared `J` nodes and needs no change. Only the extracted variable declaration is language-specific, so it now branches: Kotlin emits `val name = expr` and relies on type inference, skipping the Java type-name and import plumbing. The Kotlin template is anchored on the enclosing method body as an added statement, which forces a statement context; anchoring on the lambda's trailing call would wrap it as an expression (`var o = ...`) and fail to parse. Groovy and other languages remain excluded via `isAcceptable`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6092202 to
e93ea56
Compare
|
Thanks a lot for the help here; I'd originally trimmed our contribution a bit as Claude loves to Definitely open to accept more contributions that expand support for Kotlin in this set of recipes; you'll find a mixture of things that work out of the box, whilst other areas might indeed need small changes. Wherever possible we try to solve these generically, even when using JavaTemplate for other languages. So definitely let us know if you spot repeated patterns that we'd rather pull up rather than fix in individual recipes. |
Two follow-ups to the in-house OpenRewrite runner.
disabledRecipes lets a single recipe be switched off by name, e.g.
openrewrite {
disabledRecipes.add("org.openrewrite.java.testing.junit5.AssertThrowsOnLastStatement")
}
The compiled composites (JUnit5BestPractices and friends) expose an immutable
recipeList, so the runner wraps the recipe tree in a FilteringRecipe that hides
the disabled names instead of mutating the list. This removes the need to fork a
recipe module or drop a whole composite; the JUnit 5 recipes are now enabled for
every test module and only AssertThrowsOnLastStatement is switched off, pending
openrewrite/rewrite-testing-frameworks#1048
rewriteDiagnose runs each active leaf recipe on its own and reports whether it
fails, would change files, or does nothing, so the problematic recipes can be
found without reading a full run. On jorphan: 183 leaf recipes, 0 fail in Java
mode, 21 would make changes. Note that a recipe which only fails as part of an
ordered composite (AssertThrowsOnLastStatement on Kotlin) passes in isolation, so
the per-leaf report is a lower bound; the composite run surfaces the rest as
non-fatal "Error while rewriting" warnings thanks to the lenient ExecutionContext.
-PopenrewriteKotlin=true flips Kotlin processing on from the command line for
experimenting with rewrite-kotlin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rom 3.38.0 to 3.42.0 [skip ci] Bumps [org.openrewrite.recipe:rewrite-testing-frameworks](https://github.com/openrewrite/rewrite-testing-frameworks) from 3.38.0 to 3.42.0. Release notes *Sourced from [org.openrewrite.recipe:rewrite-testing-frameworks's releases](https://github.com/openrewrite/rewrite-testing-frameworks/releases).* > 3.42.0 > ------ > > What's Changed > -------------- > > * Support Kotlin sources in `AssertThrowsOnLastStatement` by [`@vlsi`](https://github.com/vlsi) in [openrewrite/rewrite-testing-frameworks#1048](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1048) > * Use range selector so JUnit5to6Migration gates on Java 17 or higher by [`@steve-aom-elliott`](https://github.com/steve-aom-elliott) in [openrewrite/rewrite-testing-frameworks#1049](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1049) > * Simplify JUnit5to6Migration minimum Java version to '17' by [`@steve-aom-elliott`](https://github.com/steve-aom-elliott) in [openrewrite/rewrite-testing-frameworks#1050](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1050) > > New Contributors > ---------------- > > * [`@vlsi`](https://github.com/vlsi) made their first contribution in [openrewrite/rewrite-testing-frameworks#1048](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1048) > > **Full Changelog**: <openrewrite/rewrite-testing-frameworks@v3.41.0...v3.42.0> > > 3.41.0 > ------ > > What's Changed > -------------- > > * Keep `mockStatic` import in `CleanupMockitoImports` when type info is incomplete by [`@timtebeek`](https://github.com/timtebeek) in [openrewrite/rewrite-testing-frameworks#1047](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1047) > > **Full Changelog**: <openrewrite/rewrite-testing-frameworks@v3.40.0...v3.41.0> > > v3.39.0 > ------- > > What's Changed > -------------- > > * Add `PowerMockWhiteboxGetFieldToJavaReflection` recipe by [`@MBoegers`](https://github.com/MBoegers) in [openrewrite/rewrite-testing-frameworks#1025](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1025) > * Add `PowerMockWhiteboxGetMethodToJavaReflection` recipe by [`@MBoegers`](https://github.com/MBoegers) in [openrewrite/rewrite-testing-frameworks#1026](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1026) > * Add `PowerMockWhiteboxInvokeConstructorToJavaReflection` recipe by [`@MBoegers`](https://github.com/MBoegers) in [openrewrite/rewrite-testing-frameworks#1027](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1027) > * Expand TestNG to AssertJ assertion coverage by [`@juherr`](https://github.com/juherr) in [openrewrite/rewrite-testing-frameworks#1028](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1028) > * Flag un-migratable PowerMock powermock-reflect usage with a comment by [`@MBoegers`](https://github.com/MBoegers) in [openrewrite/rewrite-testing-frameworks#1016](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1016) > > New Contributors > ---------------- > > * [`@juherr`](https://github.com/juherr) made their first contribution in [openrewrite/rewrite-testing-frameworks#1028](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/pull/1028) > > **Full Changelog**: <openrewrite/rewrite-testing-frameworks@v3.38.0...v3.39.0> Commits * [`2b5d852`](openrewrite/rewrite-testing-frameworks@2b5d852) Keep Mockito dependency managed in Mockito1to3Migration multi-module builds * [`e2dffca`](openrewrite/rewrite-testing-frameworks@e2dffca) OpenRewrite recipe best practices * [`c709cec`](openrewrite/rewrite-testing-frameworks@c709cec) Simplify JUnit5to6Migration minimum Java version to '17' ([#1050](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/issues/1050)) * [`d3830c0`](openrewrite/rewrite-testing-frameworks@d3830c0) Use range selector so JUnit5to6Migration gates on Java 17 or higher ([#1049](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/issues/1049)) * [`b0b1f19`](openrewrite/rewrite-testing-frameworks@b0b1f19) Support Kotlin sources in `AssertThrowsOnLastStatement` ([#1048](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/issues/1048)) * [`2e6d485`](openrewrite/rewrite-testing-frameworks@2e6d485) OpenRewrite recipe best practices * [`95ff73a`](openrewrite/rewrite-testing-frameworks@95ff73a) Keep `mockStatic` import in `CleanupMockitoImports` when type info is incompl... * [`cba9383`](openrewrite/rewrite-testing-frameworks@cba9383) Recipe best practices * [`9321cbc`](openrewrite/rewrite-testing-frameworks@9321cbc) Convert `assertThrows` with an `Executable` variable ([#1046](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/issues/1046)) * [`0eb4765`](openrewrite/rewrite-testing-frameworks@0eb4765) Skip JUnit 4 lifecycle annotation when Jupiter equivalent already present ([#1](https://redirect.github.com/openrewrite/rewrite-testing-frameworks/issues/1)... * Additional commits viewable in [compare view](openrewrite/rewrite-testing-frameworks@v3.38.0...v3.42.0) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Why
AssertThrowsOnLastStatementis aJavaTemplate-based recipe, and Java visitors also run over Kotlin becauseK.CompilationUnitimplementsJavaSourceFile. On a Kotlin file the recipe emitted Java syntax for the extracted variable declaration — producing invalid code such asbaz: String = baz()— or failed outright withExpected a template that would generate exactly one statement to replace one statement, but generated 0. This shows up in mixed Java/Kotlin projects, where the recipe runs as part of thejunit-jupitermigration rather than in isolation.What
The statement-hoisting logic already works on shared
Jnodes, so it needs no change. Only the extracted variable declaration is language-specific, so it now branches:val name = exprand relies on type inference, skipping the Java type-name and import plumbing.var o = ...) and fail to parse, because Kotlin models a bareassertThrows(...)call-statement as an expression.isAcceptablekeeps Groovy and other languages excluded, sinceJavaTemplatewould still corrupt them.How to verify
./gradlew test --tests 'org.openrewrite.java.testing.junit5.AssertThrowsOnLastStatementTest'The Kotlin behaviour is pinned by new tests covering argument extraction as an inferred
val, leading-statement hoisting, unique names for multiple extracted arguments, collision with an existing variable, a preserved message argument, a single-statement lambda, field-access arguments left in place, and the reifiedassertThrows<T> { }form left untouched. Java edge cases (nestedassertThrows, factory-method naming) are covered too.🤖 Generated with Claude Code