Hello, I have an interface for `Firebase` events a...
# detekt
n
Hello, I have an interface for
Firebase
events and I want to check whether or not the passed
id
parameter exceeds 40 char limit. What function of a custom detekt rule(e.g.
visitClassBody
) should I use? Sample usages of different calls So how can I access to the
create
function and check its
id
property?
Copy code
interface FirebaseTrackingEvent : TrackingEvent {
    companion object {
        fun create(firebaseId: String, params: Map<String, Any> = emptyMap()): FirebaseTrackingEvent {
            return FirebaseTrackingEventDataClass(firebaseId, params)
        }
    }

object SampleTrackingEvents {

    val sampleEvent = FirebaseTrackingEvent.create(id = "sample_event_id_which_is_more_than_40_char")

    fun sampleEvent1(tabIndex: Int): TrackingEvent {
       return FirebaseTrackingEvent.create(
           firebaseId = "sample_event_id_which_is_more_than_40_char",
           params = mapOf("index" to tabIndex)
       )
   }
}
g
You should use
visitCallExpression
I will start by copying this: https://github.com/detekt/detekt/blob/7cb2459c85269e542bee261ce268a2bf3e48f607/det[…]/io/gitlab/arturbosch/detekt/rules/complexity/NamedArguments.kt You need to instead: 1. Check if the method invocation is the
.create
you want (you might need Type Resolution for this, depends on how ‘precise’ you want to be) 2. Iterate through the params, find the first called
firebaseId
3. Check the lenght of the string (if it’s a string. If it’s not a string, like it’s a variable… then it gets complicated).
This also feels something like a runtime check 🤔
n
Thank you Nicola, will take a look! What if the parameter isn’t a
named
argument? How can I make sure I get the correct one always? Also wdym by a runtime check?
I get this error when using
getResolvedCall
, do you know why?
Copy code
No resolved call for 'create(firebaseId = "sample_event_id_which_is_more_than_40_char")' at (7,61) in /Test.kt
java.lang.AssertionError: No resolved call for 'create(firebaseId = "sample_event_id_which_is_more_than_40_char")' at (7,61) in /Test.kt
g
What if the parameter isn’t a
named
argument?
That’s not a problem as
visitCallExpression
will allow you to visit all the method calls, regardless of number of parameters, named parameters and so on.
How can I make sure I get the correct one always?
That’s the ‘challenging’ part. I would check that the method name is called
create
. The challenging part here is that you could have a false positive and catch other ‘create’ methods from other APIs. That’s where having Type Resolution would help.
Also wdym by a runtime check?
I meant that you should just check inside your application that the parameter has the correct lenght and fire a warning maybe? Like I understand the intention to do it with a static analyser, but what happens if a user is using a function to compute this value say randomly? Static analysis can’t help you here.
I get this error when using
getResolvedCall
, do you know why?
Hard to say without seeing your code.
n
That’s the ‘challenging’ part. I would check that the method name is called
create
. The challenging part here is that you could have a false positive and catch other ‘create’ methods from other APIs. That’s where having Type Resolution would help.
I try to narrow down results by
Copy code
expression.parent.firstChild.textMatches("FirebaseTrackingEvent")
expression.calleeExpression?.textMatches("create") ?: false
Is this a correct approach?
Hard to say without seeing your code.
This is the simplest code for your reference. It throws exception
Copy code
package com.example.sample

    import com.example.FirebaseTrackingEvent

    object SampleTrackingEvents {

        val sampleEvent = FirebaseTrackingEvent.create(firebaseId = "sample_event_id_which_is_more_than_40_char")
    }
Copy code
override fun visitCallExpression(expression: KtCallExpression) {
    expression.getResolvedCallWithAssert(bindingContext)
}
I meant that you should just check inside your application that the parameter has the correct lenght and fire a warning maybe? Like I understand the intention to do it with a static analyser, but what happens if a user is using a function to compute this value say randomly? Static analysis can’t help you here.
Fortunately, all keys/ids are hardcoded and passed prior, we don’t have such a mechanism to compute randomly. Therefore
detekt
would be the most useful tool here in order to catch incorrect usages on CI side and fail PRs.
g
I try to narrow down results by
It is, but just as a reminder, the user can also
Copy code
import ...FirebaseTrackingEvent.create

fun main() {
    create(...)
}
And you won’t be capturing that.
As for your exception, not sure about the
getResolvedCallWithAssert
as I never used it. My suggestion is to try to do some TDD, like write your tests first, and then try to work around the PSI api (which is terrible 😞). What helps is to get inspired by other Detekt rules or use the PSI viewer https://www.jetbrains.com/help/idea/psi-viewer.html
n
It is, but just as a reminder, the user can also
```import ...FirebaseTrackingEvent.create
fun main() {
create(...)
}```
And you won’t be capturing that.
Good catch but couldn’t find sufficient info and didn’t understand how type resolution would work for me here. Any more help? 🙂
As for your exception, not sure about the
getResolvedCallWithAssert
as I never used it. My suggestion is to try to do some TDD, like write your tests first, and then try to work around the PSI api (which is terrible 😞).
What helps is to get inspired by other Detekt rules or use the PSI viewer
Awesome, that tool really helped me a lot!
g
Good catch but couldn’t find sufficient info and didn’t understand how type resolution would work for me here. Any more help?
Docs is here: https://detekt.dev/docs/gettingstarted/type-resolution/ As for writing a rule there is a paragraph there.