Skip to content

Do not wrap operators onto a new line in Kotlin sources - #936

Merged
sambsnyd merged 1 commit into
openrewrite:mainfrom
vlsi:kotlin-operatorwrap
Jul 23, 2026
Merged

sambsnyd merged 1 commit into
openrewrite:mainfrom
vlsi:kotlin-operatorwrap

Conversation

@vlsi

@vlsi vlsi commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What's changed

OperatorWrap now only reformats Java compilation units, leaving Kotlin (and other non-Java JavaSourceFiles) untouched. Adds a Kotlin test.

Why

In Kotlin a newline terminates an expression, so a leading binary operator is parsed as a unary operator on a new statement. With the default WrapOption.NL style the recipe moves a binary operator to the start of the next line:

val s = "aaa" + "b" +
    "c"

becomes

val s = "aaa" + "b"
    + "c"

The trailing + "c" is now a unaryPlus on a separate statement, so the code no longer compiles. This surfaced running a CodeCleanup composite over Apache JMeter's mixed Java/Kotlin sources — a letsPlot(...) + geomLine {...} + ... chain was broken the same way.

The recipe's visitBinary moves operators purely by relocating whitespace, which is only valid under Java's newline rules. Restricting the visitor to J.CompilationUnit (rather than every JavaSourceFile) keeps the Java behaviour and stops it corrupting Kotlin.

How to verify

./gradlew test --tests "*.OperatorWrapKotlinTest" --tests "*.OperatorWrapTest" — the new Kotlin test passes and the existing Java suite (19 tests) is unchanged.

In Kotlin a newline terminates an expression, so a leading binary operator is
parsed as a unary operator on a new statement. With the default `WrapOption.NL`
style, `OperatorWrap` moved a binary operator to the start of the next line,
turning

    val s = "aaa" + "b" +
        "c"

into the uncompilable

    val s = "aaa" + "b"
        + "c"

(the leading `+` becomes `unaryPlus`). This surfaced running a `CodeCleanup`
composite over Apache JMeter's mixed Java/Kotlin sources.

Restrict the visitor to `J.CompilationUnit` so it only reformats Java and leaves
Kotlin (and other non-Java `JavaSourceFile`s) untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vlsi
vlsi force-pushed the kotlin-operatorwrap branch from efd23fc to a4ad45b Compare July 10, 2026 22:14
@sambsnyd
sambsnyd merged commit 5545efa into openrewrite:main Jul 23, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Jul 23, 2026
mergify Bot added a commit to robfrank/linklift that referenced this pull request Aug 4, 2026
… 2.39.0 to 2.40.0 [skip ci]

Bumps [org.openrewrite.recipe:rewrite-static-analysis](https://github.com/openrewrite/rewrite-static-analysis) from 2.39.0 to 2.40.0.
Release notes

*Sourced from [org.openrewrite.recipe:rewrite-static-analysis's releases](https://github.com/openrewrite/rewrite-static-analysis/releases).*

> 2.40.0
> ------
>
> What's Changed
> --------------
>
> * Retain UnnecessaryThrows on overridable methods ([#939](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/939)) by [`@​timtebeek`](https://github.com/timtebeek) in [openrewrite/rewrite-static-analysis#940](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/940)
> * Qualify `this` for outer-class methods in ReplaceLambdaWithMethodReference by [`@​timtebeek`](https://github.com/timtebeek) in [openrewrite/rewrite-static-analysis#942](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/942)
> * FixStringFormatExpressions: don't strip args with no matched specifiers by [`@​neil-mushell`](https://github.com/neil-mushell) in [openrewrite/rewrite-static-analysis#943](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/943)
> * UnnecessaryExplicitTypeArguments: retain load-bearing witness in argument position on instance methods too by [`@​neil-mushell`](https://github.com/neil-mushell) in [openrewrite/rewrite-static-analysis#946](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/946)
> * Set Develocity `projectId` to "openrewrite" by [`@​timtebeek`](https://github.com/timtebeek) in [openrewrite/rewrite-static-analysis#947](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/947)
> * Add recipe for finding public methods missing javadoc by [`@​sambsnyd`](https://github.com/sambsnyd) in [openrewrite/rewrite-static-analysis#948](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/948)
> * Add recipes for migrating off a variety of legacy collection types by [`@​sambsnyd`](https://github.com/sambsnyd) in [openrewrite/rewrite-static-analysis#949](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/949)
> * exception-without-cause by [`@​sambsnyd`](https://github.com/sambsnyd) in [openrewrite/rewrite-static-analysis#950](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/950)
> * Do not wrap operators onto a new line in Kotlin sources by [`@​vlsi`](https://github.com/vlsi) in [openrewrite/rewrite-static-analysis#936](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/936)
> * Do not remove casts from raw types to their parameterized form by [`@​timtebeek`](https://github.com/timtebeek) in [openrewrite/rewrite-static-analysis#954](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/954)
> * Forward Code Genome Project publish credentials to `ci-gradle.yml` by [`@​jkschneider`](https://github.com/jkschneider) in [openrewrite/rewrite-static-analysis#955](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/955)
>
> New Contributors
> ----------------
>
> * [`@​neil-mushell`](https://github.com/neil-mushell) made their first contribution in [openrewrite/rewrite-static-analysis#943](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/943)
> * [`@​vlsi`](https://github.com/vlsi) made their first contribution in [openrewrite/rewrite-static-analysis#936](https://redirect.github.com/openrewrite/rewrite-static-analysis/pull/936)
>
> **Full Changelog**: <openrewrite/rewrite-static-analysis@v2.39.0...v2.40.0>


Commits

* [`00641d2`](openrewrite/rewrite-static-analysis@00641d2) Forward Code Genome Project publish credentials to `ci-gradle.yml` ([#955](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/955))
* [`95224f1`](openrewrite/rewrite-static-analysis@95224f1) Fix test
* [`39215cf`](openrewrite/rewrite-static-analysis@39215cf) Trim data table input
* [`e820f02`](openrewrite/rewrite-static-analysis@e820f02) Do not remove casts from raw types to their parameterized form ([#954](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/954))
* [`5545efa`](openrewrite/rewrite-static-analysis@5545efa) Do not wrap operators onto a new line in Kotlin sources ([#936](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/936))
* [`8a88b48`](openrewrite/rewrite-static-analysis@8a88b48) exception-without-cause ([#950](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/950))
* [`9188c13`](openrewrite/rewrite-static-analysis@9188c13) Add recipes for migrating off a variety of legacy collection types ([#949](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/949))
* [`bcf245c`](openrewrite/rewrite-static-analysis@bcf245c) Add recipe for finding public methods missing javadoc ([#948](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/948))
* [`8e4b8c8`](openrewrite/rewrite-static-analysis@8e4b8c8) Set Develocity `projectId` to "openrewrite" ([#947](https://redirect.github.com/openrewrite/rewrite-static-analysis/issues/947))
* [`13da514`](openrewrite/rewrite-static-analysis@13da514) Move Develocity to community.develocity.cloud
* Additional commits viewable in [compare view](openrewrite/rewrite-static-analysis@v2.39.0...v2.40.0)
  
[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility\_score?dependency-name=org.openrewrite.recipe:rewrite-static-analysis&package-manager=maven&previous-version=2.39.0&new-version=2.40.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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants