openrewrite / openrewrite/rewrite

Kotlin external classpath artifact fail to parse

Open
#5,129 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug kotlin parser
Dominant language
Java
Stars
3.7k
Forks
571
Avg merge
13h 12m
Merged PRs (30d)
261

Description

I am using

  • OpenRewrite v8.30.0
  • Gradle plugin v8.30.0
  • rewrite-kotlin v8.30.0

How are you running OpenRewrite?

Using openrewrite with kotlin/gradle plugin, single module

Following on from slack conversation at https://rewriteoss.slack.com/archives/C01A843MWG5/p1721744738864449?thread_ts=1721135504.974819&cid=C01A843MWG5

What is the smallest, simplest way to reproduce the problem?

Based on the rewrite starter recipe, I am trying to write a simple replacement that references the jar io.kotest:kotest-property-jvm, however it fails on parsing

package com.yourorg;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;
import org.openrewrite.kotlin.KotlinIsoVisitor;
import org.openrewrite.kotlin.KotlinTemplate;

@Value
@EqualsAndHashCode(callSuper = false)
public class MigrateUUIDArbUsage extends Recipe {
    @Override
    public String getDisplayName() {
        // language=markdown
        return "Convert Arb uuid usages`";
    }

    @Override
    public String getDescription() {
        return "Convert arb uuid usages.";
    }

    private static MethodMatcher MATCHER = new MethodMatcher("io.kotest.property.arbitrary.ArbsKt next(..)");

    @Override
    public TreeVisitor<?, ExecutionContext> getVisitor() {
        return Preconditions.check(new UsesMethod<>("io.kotest.property.arbitrary.ArbsKt next(..)", null),
                new KotlinIsoVisitor<ExecutionContext>() {
                    @Override
                    public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
//                        System.out.println(TreeVisitingPrinter.printTree(getCursor()));
                        J.MethodInvocation m = super.visitMethodInvocation(method, ctx);

                        if (!MATCHER.matches(m)) {
                            return m;
                        }

                        if (m.getSelect() instanceof J.Identifier && ((J.Identifier) m.getSelect()).getSimpleName().equals("uuidArb")) {
                            maybeAddImport("io.kotest.property.Arb", false);
                            maybeAddImport("io.kotest.property.arbitrary.UUIDVersion", false);
                            maybeAddImport("io.kotest.property.arbitrary.next", false);

                            m = KotlinTemplate.builder("Arb.uuid(UUIDVersion.V4, false).next()")
                                            .doBeforeParseTemplate(t -> System.out.println(t))
                                            .imports(
                                            "io.kotest.property.arbitrary.next",
                                            "io.kotest.property.arbitrary.UUIDVersion",
                                            "io.kotest.property.Arb")
                                            .javaParser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(true)
                                            .classpath(
                                                   "kotest-property-jvm"
                                            )
                                    )
                                    .build()
                                    .apply(getCursor(), m.getCoordinates().replace());
                        }
                        return m;
                    }
                });
    }
}
package com.yourorg

import org.junit.jupiter.api.Test
import org.openrewrite.DocumentExample
import org.openrewrite.kotlin.Assertions
import org.openrewrite.kotlin.KotlinParser
import org.openrewrite.test.RecipeSpec
import org.openrewrite.test.RewriteTest

class ArbTransformTest: RewriteTest {
    override fun defaults(spec: RecipeSpec) {
        spec.recipe(MigrateUUIDArbUsage())
            .parser(
                KotlinParser.builder() //.parser(
                //JavaParser.fromJavaVersion()
                    .classpath(
                        "kotest-property-jvm",
                        "kotest-property",
                        "arbs",
                        "junit-jupiter-api")
            )
    }

    @DocumentExample
    @Test
    fun kotlinFoo() {
        rewriteRun(
            Assertions.kotlin(
                """
                import io.kotest.property.arbitrary.next
                import org.junit.jupiter.api.Assertions
                import org.junit.jupiter.api.Test
                import org.smw.arbs.uuidArb
                
                class TestTest {
                    @Test
                    fun arbTest() {
                        val uuid = uuidArb.next()
                        Assertions.assertEquals(uuid, uuid)
                    }
                }

            """.trimIndent(), """
                import io.kotest.property.Arb
                import io.kotest.property.arbitrary.UUIDVersion
                import io.kotest.property.arbitrary.next
                import org.junit.jupiter.api.Assertions
                import org.junit.jupiter.api.Test
                
                class TestTest {
                    @Test
                    fun arbTest() {
                        val uuid = Arb.uuid(UUIDVersion.V4, false).next()
                        Assertions.assertEquals(uuid, uuid)
                    }
                }

            """.trimIndent()
            )
        )
    }
}

Error when running the test is

import io.kotest.property.arbitrary.next
import io.kotest.property.arbitrary.UUIDVersion
import io.kotest.property.Arb
import org.openrewrite.java.internal.template.__M__;
import org.openrewrite.java.internal.template.__P__;
class Template {
var o : Object =
/*__TEMPLATE__*/Arb.uuid(UUIDVersion.V4, false).next()/*__TEMPLATE_STOP__*/
;
}

java.lang.IllegalStateException: LST contains missing or invalid type information
Identifier->MethodInvocation->MethodInvocation->NamedVariable->VariableDeclarations->Block->MethodDeclaration->Block->ClassDeclaration->CompilationUnit
/*~~(Identifier type is missing or malformed)~~>*/Arb

	at org.openrewrite.kotlin.Assertions.assertValidTypes(Assertions.java:235)
	at org.openrewrite.kotlin.Assertions.validateTypes(Assertions.java:56)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:508)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:132)
	at org.openrewrite.test.RewriteTest.rewriteRun(RewriteTest.java:127)
	at com.yourorg.ArbTransformTest.kotlinFoo(ArbTransformTest.kt:27)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)


The artifact is included in the project via
testRuntimeOnly("io.kotest:kotest-property:5.9.1")

If I add spec.typeValidationOptions(TypeValidation.none()) to the test, it passes, ideally would like to avoid this.

Are you interested in contributing a fix to OpenRewrite?

Happy to help where I can

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the KotlinTemplate builder's JavaParser classpath handling and the KotlinParser configuration in ArbTransformTest.kt. Run kotlinFoo with type validation enabled and inspect the failure reported by Assertions.validateTypes. Done means the external Kotest artifact is resolved for the template and the test passes without TypeValidation.none().

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kotlin
Domain
testing-qa, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.