Skip to content

Do not apply Java typecast spacing to Kotlin as casts#8236

Draft
vlsi wants to merge 2 commits into
openrewrite:mainfrom
vlsi:kotlin-typecast-spacing
Draft

Do not apply Java typecast spacing to Kotlin as casts#8236
vlsi wants to merge 2 commits into
openrewrite:mainfrom
vlsi:kotlin-typecast-spacing

Conversation

@vlsi

@vlsi vlsi commented Jul 10, 2026

Copy link
Copy Markdown

What's changed

SpacesVisitor#visitTypeCast now skips type casts that are not inside a J.CompilationUnit, so the Java typecast spacing rules no longer run on Kotlin as casts. Adds a rewrite-kotlin test covering as and as?.

Why

Kotlin reuses J.TypeCast to model expr as Type, which has no parentheses. visitTypeCast assumes the Java (Type) expr layout — it sets the expression's prefix (the space after )) and applies the within-parentheses spacing to the type. On a Kotlin cast the expression is printed before as, so those edits move and drop whitespace:

val b = list.get(0) as List<*>   ->   val b =  list.get(0) asList<*>

The mangled output no longer compiles. Any Java spacing recipe run over a mixed Java/Kotlin project triggers it — this surfaced with TypecastParenPad pulled in by a CodeCleanup composite on Apache JMeter's mixed sources. The fix mirrors AutoFormatVisitor, which already restricts itself to J.CompilationUnit.

How to verify

  • ./gradlew :rewrite-kotlin:test --tests "*.TypecastParenPadKotlinTest" — fails before the change, passes after.
  • ./gradlew :rewrite-java-test:test --tests "*.SpacesTest" --tests "*.TypecastParenPadTest" — Java behaviour is unchanged (204 tests still pass).

Kotlin reuses `J.TypeCast` to model `expr as Type`, which has no parentheses.
`SpacesVisitor#visitTypeCast` assumes the Java `(Type) expr` layout: it sets the
expression's prefix (the space after `)`) and applies the within-parentheses
spacing to the type. On a Kotlin cast the expression is printed before `as`, so
those edits move and drop whitespace: `x as Foo` prints back as `x asFoo`, with a
stray space after the assignment. Any Java spacing recipe run over a mixed
Java/Kotlin project hits this, e.g. TypecastParenPad pulled in by CodeCleanup.

Skip the transformation when the cast is not inside a `J.CompilationUnit`,
matching `AutoFormatVisitor`, which already limits itself to Java sources.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Jul 10, 2026
vlsi added a commit to vlsi/jmeter that referenced this pull request Jul 10, 2026
Add an additive -PopenrewriteDisable=<comma,separated> switch so recipes can be
turned off from the command line without editing the build, and record which
Java recipes corrupt Kotlin when -PopenrewriteKotlin=true is used.

Applying the active recipes to Kotlin and compiling the result shows exactly
three Java recipes that produce non-compiling Kotlin: ShortenFullyQualified-
TypeReferences (drops a qualifier without the import), TypecastParenPad
(`x as Foo` -> `x asFoo`, root cause in openrewrite/rewrite#8236) and
OperatorWrap (leading `+` becomes a unary plus). All three are safe on Java, so
they stay active; Kotlin stays off by default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@timtebeek

Copy link
Copy Markdown
Member

hi! Thanks for the various reports and PRs in progress! Good to see what you're running up against, and see what we can do to get these resolved. In this specific case the fix might be a little more involved; we actually have language specific SpacesVisitor formatters that ought to be used. But TypecastParenPad likely predates that pattern. Let's digg a little deeper and not change the Java formatter to fix Kotlin.

@timtebeek
timtebeek marked this pull request as draft July 10, 2026 22:40
@timtebeek
timtebeek force-pushed the kotlin-typecast-spacing branch 4 times, most recently from b35cf2c to 0d9d0c3 Compare July 10, 2026 23:22
@timtebeek
timtebeek marked this pull request as ready for review July 10, 2026 23:22
Replace the `firstEnclosing(J.CompilationUnit.class)` guard in the Java
SpacesVisitor with service dispatch. `TypecastParenPad` now asks the source
file for `AutoFormatService.spacesVisitor(...)`, so each language supplies its
own visitor:

- Java: the Java SpacesVisitor (unchanged behavior).
- Groovy: the Groovy SpacesVisitor, which pads `(Type) expr` casts while
  skipping `expr as Type` casts via the AsStyleTypeCast marker. The previous
  guard bailed on Groovy entirely (G.CompilationUnit is not a
  J.CompilationUnit), silently disabling padding there.
- Kotlin: a no-op, since `expr as Type` has no parentheses to pad.

This keeps the Java SpacesVisitor language-agnostic and fixes latent
corruption of Groovy/Kotlin `as` casts.
@timtebeek
timtebeek force-pushed the kotlin-typecast-spacing branch from 0d9d0c3 to 353adad Compare July 10, 2026 23:30
@timtebeek
timtebeek marked this pull request as draft July 10, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants