Is it possible to know the type of a ArgumentExpre...
# detekt
d
Is it possible to know the type of a ArgumentExpression? Suppose I only want a specific type of object to be used as the value of a
RoundedCornerShape
. Is it possible to achieve this by finding out the variable type declared in it? More details in the thread.
Example code:
Copy code
@Composable
private fun CardWithRoundedCorners(flag: Boolean) {
    val cornerRadius by animateDpAsState(
        targetValue = if (flag) CornerRadius.none else CornerRadius.lg,
        label = "cornerRadiusAnimation",
    )

    Card(
        shape = RoundedCornerShape(cornerRadius),
    ) {}
}
In this case, cornerRadius is of type
CornerRadius
, an object I created:
Copy code
object CornerRadius {
    val none = 0.dp
    val lg = 16.dp
}
The best rule I could come up with is this one, but it only works for cases where the CornerRadius is declared directly inside the
RoundedCornerShape
constructor, not when I have a variable of type CornerRadius being passed to
RoundedCornerShape
a
I think you might be able to use binding context to get the type info of a expression.
bindingContext
is map of of complier info(only available when when used with type resolution) which contains various info around the code
d
I’ve been reading the docs and looking at some rules that do use the
getType(bindingContext)
function, but it’s not clear yet how I could achieve this. I created a unit test with
compileAndLintWithContext
but I can’t use the evaluation tool when debugging 😞 the tool can’t recognize the return type of
getType
Is it possible by coding something like this?
it.getArgumentExpression()?.getType(bindingContext)..isSubtypeOf(CornerRadius)
? I’m pretty lost, it’s my first time using type resolution 😬
a
I tested below and it was more or less working
Copy code
override fun visitCallExpression(expression: KtCallExpression) {
    expression.valueArguments.forEach {
        val superTypes = expression.getResolvedCall(bindingContext)?.getParameterForArgument(it)?.type?.supertypes()?.toList() ?: emptyList()
        FqName("compose.package.RoundedCornerShape") in superTypes.map { it.fqNameOrNull() }
    }
}
in place of
compose.package.RoundedCornerShape
you have to use your class package with class name. And you might have to update some code as well
d
Thanks for the help! However, I can’t seem to test this properly. The
getResolvedCall(bindingContext)
call always returns null when I run my test. I’m structuring my test this way:
Copy code
val code = """
    @Composable
    private fun CardWithRoundedCorners(flag: Boolean) {
        val cornerRadius by animateDpAsState(
            targetValue = if (flag) CornerRadius.none else CornerRadius.lg,
            label = "cornerRadiusAnimation",
        )
    
        Card(
            shape = RoundedCornerShape(cornerRadius),
        ) {}
    }
""".trimIndent()
val findings = WrongRoundedCornerShapeSizeProperty().compileAndLintWithContext(
    env,
    code,
)
assertEquals(0, findings.size)
I’m using JUnit 5, so I added the
@KotlinCoreEnvironmentTest
annotation to my test class along with the env in the constructor but it doesn’t work. I’ve already tried using
.lintWithContext()
and using
createEnvironment().env
, but to no avail 😕 I tried reading something about this in the docs but couldn’t find anything related to it
a
first of all in your UT does
@Composable
matter? as detekt itself compile the code AFAIK it will not apply any other Kotlin complier plugin. Also have you added the dependency of compose in your project. right?
I tried below code
Copy code
@Test
fun `abc`() {
    val code = """
        import kotlin.properties.Delegates.observable
        var cornerRadius: Int by observable(initialValue = if (true) CornerRadius.none else CornerRadius.lg) { _, _, _ ->

        }

        object CornerRadius {
            val none = 0
            val lg = 16
        }
    """.trimIndent()
    val findings = subject.compileAndLintWithContext(env, code)
    assertThat(findings).hasSize(1)
}
and able to find the types Make sure you code is compilable in your TC
d
Sorry for the delay! I’ll re-check the dependencies then. Maybe there’s something I’m missing here, I’ll keep you posted once I found out what it is. Thank you so much for your help!
👍 1