Skip to content

WIP: Use IR for KotlinParser instead of PSI/FIR#6325

Closed
Crustack wants to merge 9 commits intoopenrewrite:mainfrom
Crustack:feat/kotlin-parser-ir
Closed

WIP: Use IR for KotlinParser instead of PSI/FIR#6325
Crustack wants to merge 9 commits intoopenrewrite:mainfrom
Crustack:feat/kotlin-parser-ir

Conversation

@Crustack
Copy link
Copy Markdown
Contributor

@Crustack Crustack commented Nov 22, 2025

This is Work in Progress

What's changed?

Instead of relying on PSI/FIR for Kotlin Parsing, this proposes to leverage KotlinToJVMBytecodeCompiler.INSTANCE.analyze + JvmIrCodegenFactory.convertToIr to generate the IrFiles.

What's your motivation?

Improve Kotlin support

Anything in particular you'd like reviewers to focus on?

Since I am fairly new to the entire Kotlin Compiler eco-system and have only worked with PSI before, some input on the used methods to get the IrFiles is welcome.
Also since I haven't looked deeply into the KotlinTreeParserVisitor implementation yet, it would be good to know if we would still need PSI/FIR for some information or would IR be sufficient?

Anyone you would like to review specifically?

@shanman190

Have you considered any alternatives or workarounds?

No

Any additional context

TODOs:

  • Successfully generate IrFiles for the given source kotlin files (working KotlinTypeIrSignatureBuilderTest)
  • Remove firFile + nodes from KotlinSource (or will they be necessary still?)
  • Test KotlinTypeMapping with generated FirFiles
  • Remove firSessions from CompiledSource
  • Rewrite KotlinTreeParserVisitor via custom IrTreeVisitor?

Checklist

  • I've added unit tests to cover both positive and negative cases
  • I've read and applied the recipe conventions and best practices
  • I've used the IntelliJ IDEA auto-formatter on affected files

@github-project-automation github-project-automation bot moved this to In Progress in OpenRewrite Nov 22, 2025
@Crustack Crustack marked this pull request as draft November 22, 2025 16:34
@shanman190
Copy link
Copy Markdown
Contributor

@PhilKes , thanks for the work here. However, this seems to be using the K1 compiler which would limit options toward Kotlin 2.x work and be going in reverse of where the new development is happening by the Kotlin team in the K2 compiler.

@Crustack
Copy link
Copy Markdown
Contributor Author

Crustack commented Nov 23, 2025

@PhilKes , thanks for the work here. However, this seems to be using the K1 compiler which would limit options toward Kotlin 2.x work and be going in reverse of where the new development is happening by the Kotlin team in the K2 compiler.

Hi thanks for the feedback, I now updated the code to leverage the same logic as K2JvmCompiler does.
The KotlinTypeIrSignatureBuilderTest succeeds as it was before (IrProperty instead of IrField for KtFile's field).

My next step would be to try to update the build.gradle.kts kotlinVersion to 2.2.21, since I saw that the JVM Compiler Pipeline package was refactored once more (?).
I guess that would mean implementing a custom AbstractCliPipeline that uses the phases:
JvmConfigurationPipelinePhase then JvmFrontendPipelinePhase then JvmFir2IrPipelinePhase, which returns JvmFir2IrPipelineArtifact which includes the IrFiles

Comment on lines 87 to +88
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.regex.Pattern;

@Crustack
Copy link
Copy Markdown
Contributor Author

Crustack commented Nov 24, 2025

  • I updated the kotlinVersion for the dependenies to 2.2.21
  • Refactored the compilation code to use a custom JvmFir2IrPipeline which yields the FirFile + IrFiles.
  • Since I have not yet changed the KotlinTreeParserVisitor from PSI/FIR to IR I wanted to make sure all the tests still pass.
  • KotlinIrTypeMappingTest + KotlinParserTest
  • I noticed that e.g. for EqualsMethodUsageTest the compiler throws errors like: openRewriteFile0.kt:3:1: error: missing return statement
    fun isSame(obj1 : String, obj2: String) : Boolean {
    val isSame = obj1.equals(obj2)
    }

    Are these error supposed to be ignored, or do we always expect valid/complete Kotlin code?

Other than that I am checking what differences are in the generated FirFile before and after the compiler changes, since most recipe specific tests currently do not succeed, I am guessing there are some smaller changes to the structure and perhaps some of the K2JVMCompilerArguments still have to be fine-tuned but in general I think it looks promising so far.
Something that differs from before is that the FirSimpleFunctionImpl objects have controlFlowGraphReference= null and some other small differing fields that leads to the PsiElementAssociations having less entries in elementMap

Comment on lines +178 to +180
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
.filter(source -> !source.getSourcePath().getFileName().toString().startsWith("dependsOn-"));

configureBaseRoots(compilerConfiguration, arguments);
AtomicInteger idx = new AtomicInteger(0);
arguments.setFreeArgs(sources.stream()
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(toList()));

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Comment on lines +178 to +180
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
.filter(source -> !source.getSourcePath().getFileName().toString().startsWith("dependsOn-"));

configureBaseRoots(compilerConfiguration, arguments);
AtomicInteger idx = new AtomicInteger(0);
arguments.setFreeArgs(sources.stream()
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(toList()));

Comment on lines +187 to +189
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.filter(source -> {
return !source.getSourcePath().getFileName().toString().startsWith("dependsOn-");
});
.filter(source -> !source.getSourcePath().getFileName().toString().startsWith("dependsOn-"));

addJvmClasspathRoot(compilerConfiguration, PathUtil.getResourcePathForClass(AnnotationTarget.class));
AtomicInteger idx = new AtomicInteger(0);
arguments.setFreeArgs(sources.stream()
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(Collectors.toList()));
.map(source -> tempFile(ctx, source, idx.getAndIncrement())).collect(toList()));


import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.*;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import java.util.*;
import java.util.ArrayList;
import java.util.List;

}
JContainer<Expression> init = JContainer.build(Space.EMPTY, elems, Markers.EMPTY);
JavaType arrType = typeMapping.type(irVararg);
return new J.NewArray(randomId(), Space.EMPTY, Markers.EMPTY, null, Collections.emptyList(), init, arrType);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return new J.NewArray(randomId(), Space.EMPTY, Markers.EMPTY, null, Collections.emptyList(), init, arrType);
return new J.NewArray(randomId(), Space.EMPTY, Markers.EMPTY, null, emptyList(), init, arrType);

@@ -0,0 +1,48 @@
package org.openrewrite.kotlin.internal;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
package org.openrewrite.kotlin.internal;
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.kotlin.internal;

"K.CompilationUnit of KotlinIrTreeParserVisitor does not match output of KotlinTreeParserVisitor"
);
}
} No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

@Crustack
Copy link
Copy Markdown
Contributor Author

Since its not prudent trying to update Kotlin-Compiler and move from PSI/FIR to IR in one PR, I am closing this in favor of: #6338
After it is finished I will probably open another PR to start working on moving to IR based parsing

@Crustack Crustack closed this Nov 25, 2025
@github-project-automation github-project-automation bot moved this from In Progress to Done in OpenRewrite Nov 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants