Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Kotlin 2.1.0-Beta2 in CI #411

Merged
merged 8 commits into from
Oct 18, 2024
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ jobs:
needs: build
strategy:
matrix:
poko_sample_kotlin_version: [ 2.0.20, ~ ]
poko_sample_kotlin_version: [ 2.0.20, ~, 2.1.0-Beta2 ]
env:
poko_sample_kotlin_version: ${{ matrix.poko_sample_kotlin_version }}
steps:
- name: Check out the repo
uses: actions/checkout@v4
Expand All @@ -81,10 +83,12 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Upgrade yarn lock
if: ${{ matrix.poko_sample_kotlin_version != null }}
run: cd sample && ./gradlew kotlinUpgradeYarnLock

- name: Build sample
run: cd sample && ./gradlew build --stacktrace
env:
poko_sample_kotlin_version: ${{ matrix.poko_sample_kotlin_version }}

env:
GRADLE_OPTS: >-
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package dev.drewhamilton.poko.fir

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
Expand All @@ -20,8 +25,33 @@ internal class PokoFirExtensionSessionComponent(
}
}

private fun FirAnnotation.classId(): ClassId? =
annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.classId
private fun FirAnnotation.classId(): ClassId? {
val coneClassLikeType = try {
annotationTypeRef.coneTypeSafe<ConeClassLikeType>()
} catch (noSuchMethodError: NoSuchMethodError) {
val annotationTypeRef = annotationTypeRef
if (annotationTypeRef is FirResolvedTypeRef) {
// The `coneTypeSafe` inline function uses a getter that changes names from `type`
// to `coneType` in 2.1+, so we access the latter via reflection here:
FirResolvedTypeRef::class.java
.methods
.single { it.name == "getConeType" }
.invoke(annotationTypeRef)
as? ConeClassLikeType
} else {
null
}
}
return coneClassLikeType?.classId
}

@OptIn(ExperimentalContracts::class)
private inline fun <reified T : ConeKotlinType> FirTypeRef.coneTypeSafeReflection(): T? {
contract {
returnsNotNull() implies (this@coneTypeSafeReflection is FirResolvedTypeRef)
}
return (this as? FirResolvedTypeRef)?.type as? T
}

internal companion object {
internal fun getFactory(pokoAnnotation: ClassId): Factory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ import org.jetbrains.kotlin.ir.builders.irFalse
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irIfThenElse
import org.jetbrains.kotlin.ir.builders.irIfThenReturnFalse
import org.jetbrains.kotlin.ir.builders.irIfThenReturnTrue
import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.builders.irIs
import org.jetbrains.kotlin.ir.builders.irNotEquals
import org.jetbrains.kotlin.ir.builders.irNotIs
import org.jetbrains.kotlin.ir.builders.irReturnFalse
import org.jetbrains.kotlin.ir.builders.irReturnTrue
import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.builders.irWhen
Expand All @@ -29,6 +28,8 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrBranch
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
Expand Down Expand Up @@ -57,8 +58,8 @@ internal fun IrBlockBodyBuilder.generateEqualsMethodBody(
val irType = irClass.defaultType
fun irOther(): IrExpression = IrGetValueImpl(functionDeclaration.valueParameters.single())

+irIfThenReturnTrue(irEqeqeq(functionDeclaration.receiver(), irOther()))
+irIfThenReturnFalse(irNotIs(irOther(), irType))
+irIfThenReturnBool(true, irEqeqeq(functionDeclaration.receiver(), irOther()))
+irIfThenReturnBool(false, irNotIs(irOther(), irType))

val otherWithCast = irTemporary(irImplicitCast(irOther(), irType), "other_with_cast")
for (property in classProperties) {
Expand All @@ -81,7 +82,7 @@ internal fun IrBlockBodyBuilder.generateEqualsMethodBody(
irNotEquals(arg1, arg2)
}
}
+irIfThenReturnFalse(irNotEquals)
+irIfThenReturnBool(false, irNotEquals)
}
+irReturnTrue()
}
Expand Down Expand Up @@ -226,3 +227,14 @@ private fun IrBuilderWithScope.findContentDeepEqualsFunctionSymbol(
} ?: false
}
}

// TODO: Replace with direct call to irIfThenReturnTrue/False when support for 2.0.x is dropped
private fun IrBuilderWithScope.irIfThenReturnBool(
bool: Boolean,
condition: IrExpression,
): IrWhenImpl {
val thenPart = if (bool) irReturnTrue() else irReturnFalse()
return IrWhenImpl(startOffset, endOffset, context.irBuiltIns.unitType, null).apply {
branches.add(IrBranchImpl(startOffset, endOffset, condition, thenPart))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.irBranch
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irCallOp
import org.jetbrains.kotlin.ir.builders.irElseBranch
import org.jetbrains.kotlin.ir.builders.irEqualsNull
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.builders.irIfNull
Expand All @@ -25,6 +27,8 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.IrBranch
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrWhenImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
Expand Down Expand Up @@ -116,7 +120,7 @@ private fun IrBlockBodyBuilder.getHashCodeOfProperty(
val field = property.backingField!!
val irGetField = { irGetField(function.receiver(), field) }
return when {
property.type.isNullable() -> irIfNull(
property.type.isNullable() -> irIfNullCompat(
type = context.irBuiltIns.intType,
subject = irGetField(),
thenPart = irInt(0),
Expand All @@ -126,6 +130,19 @@ private fun IrBlockBodyBuilder.getHashCodeOfProperty(
}
}

// TODO: Revert to standard irIfNull when 2.0.x support is dropped
private fun IrBuilderWithScope.irIfNullCompat(
type: IrType,
subject: IrExpression,
thenPart: IrExpression,
elsePart: IrExpression,
): IrWhenImpl {
return IrWhenImpl(startOffset, endOffset, type, null).apply {
branches.add(IrBranchImpl(startOffset, endOffset, irEqualsNull(subject), thenPart))
branches.add(irElseBranch(elsePart))
}
}

/**
* Symbol-retrieval adapted from
* [org.jetbrains.kotlin.fir.backend.generators.DataClassMembersGenerator].
Expand Down
10 changes: 10 additions & 0 deletions sample/buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../../gradle/libs.versions.toml"))

//region Duplicated in ../settings.gradle
fun String.nullIfBlank(): String? = if (isNullOrBlank()) null else this

// Compile sample project with different Kotlin version than Poko, if provided:
val kotlinVersionOverride = System.getenv()["poko_sample_kotlin_version"]?.nullIfBlank()
kotlinVersionOverride?.let { kotlinVersion ->
version("kotlin", kotlinVersion)
}
//endregion
}
}
}
5 changes: 4 additions & 1 deletion sample/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,16 @@ dependencyResolutionManagement {
create("libs") {
from(files("../gradle/libs.versions.toml"))

//region Duplicated in buildSrc/settings.gradle
fun String.nullIfBlank(): String? = if (isNullOrBlank()) null else this

// Compile sample project with different Kotlin version than Poko, if provided:
val kotlinVersionOverride = System.getenv()["poko_sample_kotlin_version"]?.nullIfBlank()
kotlinVersionOverride?.let { kotlinVersion ->
version("kotlin", kotlinVersion)
}
//endregion
}
}
}

private fun String.nullIfBlank(): String? = if (isNullOrBlank()) null else this