Skip to content

Fix TypeTable round-trip for Kotlin top-level functions#7422

Draft
steve-aom-elliott wants to merge 1 commit intomainfrom
fix/kotlin-top-level-method-type
Draft

Fix TypeTable round-trip for Kotlin top-level functions#7422
steve-aom-elliott wants to merge 1 commit intomainfrom
fix/kotlin-top-level-method-type

Conversation

@steve-aom-elliott
Copy link
Copy Markdown
Contributor

Summary

Three interlocking bugs in TypeTable caused Kotlin top-level functions (top-level regular functions and extension functions) to lose method type attribution when KotlinParser received a classpath sourced from a TypeTable-synthesized directory (as mod does for downloaded LSTs) rather than the original jar. The symptom: J.MethodInvocation.getMethodType() returns null for calls like jacksonObjectMapper() (from jackson-module-kotlin), and MethodMatcher/UsesMethod therefore cannot match them.

The three fixes

  1. ACC_SYNTHETIC methods were filtered out. Kotlin extension functions compile to public static final synthetic on a *Kt facade class, so they were being dropped at write time. Now filter on ACC_PRIVATE only (and skip <clinit>), matching what's needed for type attribution.

  2. InnerClasses attribute was not captured. Kotlin FIR silently fails to resolve methods on a class whose @kotlin.Metadata.d1 protobuf references inner classes (lambdas, companion objects, etc.) unless those inner classes are declared in the class's InnerClasses attribute. The Writer's ClassVisitor didn't override visitInnerClass, so every round-tripped class came out with an empty InnerClasses attribute. Now serialize each entry as name,outerName,innerName,access into a new innerClasses TSV column, and replay them on the Reader side before any other class events so ASM writes them back out.

  3. META-INF/*.kotlin_module resources were dropped. The Kotlin compiler uses these files to map package names to their facade classes (com.fasterxml.jackson.module.kotlinExtensionsKt). Without it, the resolver can't find the top-level function's owner. Store .kotlin_module files as resource rows using a classAccess = -2 sentinel with base64-encoded content in the constantValue column, plumbed through a new ResourceConsumer callback on Reader.read() so callers can persist them alongside the synthesized .class files.

Tests

  • MethodMatcherTest#libraryTopLevelFunctionPopulatesMethodType — asserts method type is non-null for jacksonObjectMapper().
  • MethodMatcherTest#usesMethodMatchesLibraryTopLevelFunction — end-to-end UsesMethod match on the Kotlin top-level extension function.
  • TypeTableTypeAnnotationsTest#columnOrderCorrect — updated for the added column.

Both Kotlin tests use KotlinParser.builder().classpath(\"jackson-module-kotlin\"), which resolves via the shipped TypeTable — exactly the code path that was broken.

Test plan

  • ./gradlew :rewrite-java:test --tests "org.openrewrite.java.internal.parser.*"
  • ./gradlew :rewrite-kotlin:test --tests "org.openrewrite.kotlin.MethodMatcherTest"
  • Full CI

Three bugs in TypeTable caused Kotlin top-level extension functions (e.g.
`jacksonObjectMapper()` from `jackson-module-kotlin`) to lose method type
attribution when their classpath was sourced from a synthesized TypeTable
rather than the original jar:

1. Writer filtered out `ACC_SYNTHETIC` methods. Kotlin extension functions
   compile to `public static final synthetic` on a `*Kt` facade class, so
   they were being dropped. Filter on `ACC_PRIVATE` only.

2. Writer did not capture `InnerClasses` attributes. Kotlin FIR silently
   fails to resolve methods on classes whose `@kotlin.Metadata.d1`
   references inner classes (lambdas, etc.) that are not declared in the
   class's `InnerClasses` attribute. Serialize `visitInnerClass` entries
   into a new `innerClasses` TSV column and replay them on the reader.

3. Writer did not preserve `META-INF/*.kotlin_module` resources, which
   the Kotlin compiler uses to map package names to facade classes.
   Store them as resource rows (`classAccess=-2` sentinel, base64 content)
   and expose them through a new `ResourceConsumer` callback on the
   reader.
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.

1 participant