Skip to content

Commit

Permalink
Reasonable names or burstValues symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
squarejesse committed Oct 28, 2024
1 parent 004bbac commit 416ca42
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package app.cash.burst.kotlin
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.containsExactly
import assertk.assertions.containsExactlyInAnyOrder
import assertk.assertions.isFalse
import assertk.assertions.isTrue
import com.tschuchort.compiletesting.JvmCompilationResult
Expand Down Expand Up @@ -306,6 +307,64 @@ class BurstKotlinPluginTest {
assertThat(baseLog).containsExactly("running 16")
baseLog.clear()
}

@Test
fun burstValuesHasReasonableSymbolName() {
val result = compile(
sourceFile = SourceFile.kotlin(
"CoffeeTest.kt",
"""
import app.cash.burst.Burst
import app.cash.burst.burstValues
import kotlin.math.PI
import kotlin.math.abs
import kotlin.test.Test
@Burst
class CoffeeTest {
@Test
fun test(
content: Any? = burstValues(
3, // No name is generated for the first value.
"5".toInt(),
"hello",
"hello".uppercase(),
CoffeeTest::class,
Float.MAX_VALUE,
PI,
String.CASE_INSENSITIVE_ORDER,
abs(1),
null,
)
) {
}
}
""",
),
)
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode, result.messages)

val baseClass = result.classLoader.loadClass("CoffeeTest")
assertThat(baseClass.testSuffixes).containsExactlyInAnyOrder(
"toInt",
"hello",
"uppercase",
"CoffeeTest",
"3_4028235E38", // Would have preferred 'MAX_VALUE', but this is constant is inlined!
"3_141592653589793", // Would have preferred 'PI', but this is constant is inlined!
"CASE_INSENSITIVE_ORDER",
"abs",
"null",
)
}

private val Class<*>.testSuffixes: List<String>
get() = methods.mapNotNull {
when {
it.name.startsWith("test_") -> it.name.substring(5)
else -> null
}
}
}

@ExperimentalCompilerApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classFqName
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.isEnumClass
import org.jetbrains.kotlin.name.NameUtils

internal sealed interface Argument {
/** True if this argument matches the default parameter value. */
Expand All @@ -59,17 +62,9 @@ private class EnumValueArgument(

private class BurstValuesArgument(
override val isDefault: Boolean,
override val name: String,
private val value: IrExpression,
private val index: Int,
) : Argument {
override val name: String
get() {
return when {
value is IrConst<*> -> value.value.toString()
else -> index.toString()
}
}

override fun expression() = value.deepCopyWithSymbols()
}

Expand Down Expand Up @@ -113,26 +108,57 @@ private fun burstValuesArguments(
burstApisCall: IrCall,
): List<Argument> {
return buildList {
val defaultExpression = burstApisCall.valueArguments[0] ?: unexpectedParameter(parameter)
add(
BurstValuesArgument(
isDefault = true,
value = burstApisCall.valueArguments[0] ?: unexpectedParameter(parameter),
index = 0,
name = defaultExpression.suggestedName() ?: "0",
value = defaultExpression,
),
)

for ((index, element) in (burstApisCall.valueArguments[1] as IrVararg).elements.withIndex()) {
val varargExpression = element as? IrExpression ?: unexpectedParameter(parameter)
add(
BurstValuesArgument(
isDefault = false,
value = element as? IrExpression ?: unexpectedParameter(parameter),
index = index + 1,
name = varargExpression.suggestedName() ?: (index + 1).toString(),
value = varargExpression,
),
)
}
}
}

/**
* Returns a short name for this expression appropriate for use in a generated symbol declaration.
*
* If this is a constant like 'hello' or '3.14', this returns the value as a string.
*
* If this is a call like `String.CASE_INSENSITIVE_ORDER` or `abs(-5)`, this returns the name of the
* called symbol (`CASE_INSENSITIVE_ORDER` or `abs`), discarding the receiver, value parameters, and
* type parameters.
*
* If this is a class reference like `String::class`, this returns the type's simple name.
*/
@UnsafeDuringIrConstructionAPI
private fun IrExpression.suggestedName(): String? {
val raw = when (this) {
is IrConst<*> -> value.toString()
is IrCall -> {
val target = (symbol.owner.correspondingPropertySymbol?.owner ?: symbol.owner)
target.name.asString()
}

is IrClassReference -> classType.classFqName?.shortName()?.asString() ?: return null
else -> return null
}

// Calling sanitizeAsJavaIdentifier is necessary but not sufficient. We assume further phases of
// the compiler will make the returned name safe for the ultimate compilation target.
return NameUtils.sanitizeAsJavaIdentifier(raw)
}

@UnsafeDuringIrConstructionAPI
private fun enumValueArguments(
referenceClass: IrClass,
Expand Down

0 comments on commit 416ca42

Please sign in to comment.