nuhkoca
10/14/2022, 9:07 PMFirebase
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?
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)
)
}
}
gammax
10/14/2022, 11:37 PMvisitCallExpression
gammax
10/14/2022, 11:39 PM.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).gammax
10/14/2022, 11:41 PMnuhkoca
10/15/2022, 11:44 AMnamed
argument? How can I make sure I get the correct one always?
Also wdym by a runtime check?nuhkoca
10/15/2022, 12:20 PMgetResolvedCall
, do you know why?
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
gammax
10/15/2022, 12:40 PMWhat if the parameter isn’t aThat’s not a problem asargument?named
visitCallExpression
will allow you to visit all the method calls, regardless of number of parameters, named parameters and so on.gammax
10/15/2022, 12:41 PMHow 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.gammax
10/15/2022, 12:42 PMAlso 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.
gammax
10/15/2022, 12:42 PMI get this error when usingHard to say without seeing your code., do you know why?getResolvedCall
nuhkoca
10/15/2022, 2:40 PMThat’s the ‘challenging’ part. I would check that the method name is calledI try to narrow down results by. 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.create
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
package com.example.sample
import com.example.FirebaseTrackingEvent
object SampleTrackingEvents {
val sampleEvent = FirebaseTrackingEvent.create(firebaseId = "sample_event_id_which_is_more_than_40_char")
}
override fun visitCallExpression(expression: KtCallExpression) {
expression.getResolvedCallWithAssert(bindingContext)
}
nuhkoca
10/15/2022, 2:42 PMI 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.gammax
10/15/2022, 4:16 PMI try to narrow down results byIt is, but just as a reminder, the user can also
import ...FirebaseTrackingEvent.create
fun main() {
create(...)
}
And you won’t be capturing that.gammax
10/15/2022, 4:18 PMgetResolvedCallWithAssert
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.htmlnuhkoca
10/16/2022, 11:38 AMIt 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? 🙂
nuhkoca
10/16/2022, 11:38 AMAs for your exception, not sure about theas 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 😞).getResolvedCallWithAssert
What helps is to get inspired by other Detekt rules or use the PSI viewerAwesome, that tool really helped me a lot!
gammax
10/16/2022, 1:42 PMGood 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.