从 Kotlin 编译器 API 的变化开始: 2.4.0

大家好!众所周知,我有在平时维护一个简单的Kotlin编译器插件项目: Kotlin Suspend Transform Compiler Plugin。 想必经常维护编译器插件的小伙伴们也清楚,每次 Kotlin 的主要版本递进,编译器的API都会或多或少的发生变化, 也给编译器插件的更新维护带来不小的挑战。那么借此机会,我会在每次发生API变化的更新出现后, 藉由此系列记录一下能有哪些编译器API的变化可以被我发现。

不算是一种技术分享文,而是一种记录文,所以不保证有什么技术含金量喔~

今天要记录的版本变化是:v2.3.20 -> 2.4.0。

不同于以往,这次在 Gradle sync 阶段 buildSrc 就开始出错了:

bash 复制代码
> Task :buildSrc:compileKotlin FAILED
e: file:///suspend-transform-kotlin-compile-plugin/buildSrc/src/main/kotlin/GradleSupportHelper.kt:29:70 Using 'KOTLIN_1_9' is an error. Unsupported
e: file:///suspend-transform-kotlin-compile-plugin/buildSrc/src/main/kotlin/GradleSupportHelper.kt:30:75 Using 'KOTLIN_1_9' is an error. Unsupported

不过当然,这在看到 2.4.0 的更新日志的时候我就已经有预期了。从 Kotlin 2.4.0 开始,-language-version=1.9 就不再被支持了, 也就是说 K1 编译器从现在开始正式地不再支持了。

出错的这部分代码在 buildSrc 中是一个辅助用的扩展函数:

Kotlin 复制代码
fun org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension.configGradleBuildSrcFriendly() {
    coreLibrariesVersion = "1.9.0"
    compilerOptions {
        apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
        languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
    }
}

因为在之前我注意到 Gradle 内置的 Kotlin 版本通常会略低,当我 K2 编译器发布了好几个版本了,但是在实际应用的 Kotlin 项目中, Gradle 内置的 Kotlin 版本仍然是 1.9.x,这就导致如果我打算通过 buildSrc 这类方式配置编译器插件的话,会导致低版本的 Kotlin 无法使用高版本产物的尴尬局面。 因此,为了能更高地支持较大范围内的 Gradle 版本,我为 gradle plugin 模块手动降低了编译产物的 Kotlin API 版本。

不过现在,1.9 是不能用了,所以让我们把它更新到 K2,然后再继续观察后续错误吧。


好了,回到我们的主线上来,来列举一下这次更新出现的所有错误和警告:

less 复制代码
w: -Xjvm-default is deprecated. Use -jvm-default instead.
w: The argument '-Xcontext-parameters' is redundant for the current language version 2.4.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/ir/SuspendTransformTransformerPostProcessing.kt:54:17 Argument type mismatch: actual type is 'IrConstructorCall', but 'IrAnnotation' was expected.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt:32:56 Unresolved reference 'allParameters'.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt:361:11 Unresolved reference 'allParameters'.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt:362:20 Unresolved reference 'name'.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt:364:20 Unresolved reference 'type'.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/AnnotationDescriptorUtils.kt:169:39 Too many arguments for 'fun FirAnnotation.getStringArgument(name: Name): String?'.
e: file:///suspend-transform-kotlin-compile-plugin/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/AnnotationDescriptorUtils.kt:183:40 Too many arguments for 'fun FirAnnotation.getBooleanArgument(name: Name): Boolean?'.

这次的变更大概就是分为了两类:签名(参数)不匹配的(Argument type mismatch 和 Too many arguments),和符号变化的(Unresolved reference)

让我们挨个来看看。

签名变化

IrFunction.annotations

Kotlin 复制代码
internal fun SuspendTransformTransformer.postProcessGenerateOriginFunction(
    function: IrFunction,
    originFunctionIncludeAnnotations: List<IncludeAnnotation>
) {
    function.annotations = buildList {
        val currentAnnotations = function.annotations
        fun hasAnnotation(name: org.jetbrains.kotlin.name.FqName): Boolean =
            currentAnnotations.any { a -> a.hasEqualAnnotationFqName(name) }
        addAll(currentAnnotations)

        originFunctionIncludeAnnotations.forEach { include ->
            val classId = include.classInfo.toClassId()
            val annotationClass = pluginContext.referenceClass(classId) ?: return@forEach
            if (!include.repeatable && hasAnnotation(classId.asSingleFqName())) {
                return@forEach
            }

            // ❌ Argument type mismatch: actual type is 'IrConstructorCall', but 'IrAnnotation' was expected.
            add(pluginContext.createIrBuilder(function.symbol).irAnnotationConstructor(annotationClass))
        }
    }
}

从原本将一个 irAnnotationConstructor(annotationClass) 添加到一个 List 中来看,这里发生变化的应该是 IrFunction.annotations 这个列表的元素类型,从 IrConstructorCall 变为了 IrAnnotation。

语义上 annotations 从'一些构造器调用'变成了'一些注解',似乎也更符合其本意。

那么我们稍作调整即可。

Kotlin 复制代码
internal fun SuspendTransformTransformer.postProcessGenerateOriginFunction(
    function: IrFunction,
    originFunctionIncludeAnnotations: List<IncludeAnnotation>
) {
    function.annotations = buildList {
        val currentAnnotations = function.annotations
        fun hasAnnotation(name: org.jetbrains.kotlin.name.FqName): Boolean =
            currentAnnotations.any { a -> a.hasEqualAnnotationFqName(name) }
        addAll(currentAnnotations)

        originFunctionIncludeAnnotations.forEach { include ->
            val classId = include.classInfo.toClassId()
            val annotationClass = pluginContext.referenceClass(classId) ?: return@forEach
            if (!include.repeatable && hasAnnotation(classId.asSingleFqName())) {
                return@forEach
            }

            val firstAnnotationConstructor = annotationClass.constructors.first()
            add(pluginContext.createIrBuilder(function.symbol).irAnnotation(firstAnnotationConstructor))
        }
    }
}

// AnnotationDescriptorUtils.kt

fun IrBuilderWithScope.irAnnotation(symbol: IrConstructorSymbol): IrAnnotation {
    return irAnnotation(symbol)
}

FirAnnotation.getStringArgument

上面提到的那个工具类 AnnotationDescriptorUtils.kt 中正好也包含了剩下的一个错误: org.jetbrains.kotlin.fir.declarations.FirAnnotationUtils.kt 中提供的辅助扩展函数 fun FirAnnotation.getStringArgument(name: Name, session: FirSession) 不再需要第二个参数了。

它们现在的定义如下:

Kotlin 复制代码
@Deprecated(
    message = "Use getStringArgument overload without session parameter",
    replaceWith = ReplaceWith("getStringArgument(name)"),
    level = DeprecationLevel.HIDDEN
)
fun FirAnnotation.getStringArgument(name: Name, session: FirSession): String? = getStringArgument(name)
fun FirAnnotation.getStringArgument(name: Name): String? = getPrimitiveArgumentValue(name)

倒是也没怎么解释,反正就是告诉你:获取注解上的参数不需要 session 了。那还说什么了兄弟,换了。

符号变化

接下来是我们的老朋友:Unresolved reference xxx。现在的我还没开始看,但是我猜八九不离十是这些函数挪了挪地方。

org.jetbrains.kotlin.backend.common.descriptors.allParameters

果不其然。那四个 Unresolved reference xxx 的本质原因是 org.jetbrains.kotlin.backend.common.descriptors.allParameters 被移动到了 import org.jetbrains.kotlin.descriptors.konan.allParameters。 修改为更正确的 import 即可。

尾声

这次编译器的变化对我来说竟然意外地仁慈,那些新产生的特性也没有对 API 造成大范围的损坏。 或许这也是 K2 编译器正在逐步走向更加健壮文档与成熟的另一种佐证?

相关推荐
朱涛的自习室5 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
冬奇Lab7 小时前
一天一个开源项目(第237篇):DeepSeek Bot —— 把 DeepSeek Harness 变成一个有记忆、能群聊、会互相派活的 Bot 团队
人工智能·开源·deepseek
用户7273744138849 小时前
JSON 转义字符到底该怎么读:几种常见场景
github
程序员老赵9 小时前
Docker 部署 Wiki.js:轻松搭建私有化维基知识库
docker·容器·开源
.Bryce.9 小时前
在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“
android·java·服务器·redis·后端·agent·a2a
SL_staff10 小时前
考勤同步工资核算总出错?破解企业级精准自动化的三个刚性支点
mysql·github·全栈
独孤九剑打醒他10 小时前
【原创开源·航空终极篇】氢能航空动力升级:双燃料掺氢、富氧补气与露天储氢安全模型
前端·架构·开源·硬件工程·能源
webxin66610 小时前
汽水music自动看广告领取VIP
程序员·github
方舟小孩11 小时前
开源办公软件海豹办公 1.9.15 实战 从模板异常到 Android WebView 资源回归
android