Paul Dingemans
11/27/2023, 4:46 PMstopTraversalOfAST()
should indeed not be used because you want to inspect every function in the file. Most common way rules are written is to override:
override fun beforeVisitChildNodes(
node: ASTNode,
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
) {
}
This function is called for each ASTNode in the file. For each ASTNode you have to check wether it is of type FUN
and whether it contains an IDENTIFIER
with name doSomething
. If so, then apply your logic. Otherwise ignore the node an do nothing.Ruben Quadros
11/28/2023, 4:32 AMdoSomething
function. How do I ignore the whole doSomething
function? Here is my current rule:Ruben Quadros
11/28/2023, 4:33 AMoverride fun beforeVisitChildNodes(
node: ASTNode,
autoCorrect: Boolean,
emit: (
offset: Int,
errorMessage: String,
canBeAutoCorrected: Boolean
) -> Unit
) {
if (node.isAssertNotFinishingFunction()) {
//do not stop. Just skip this function.
stopTraversalOfAST()
return
}
val dotQualifiedExpression = node.psi as? KtDotQualifiedExpression ?: return
val assertThatPsi = AssertThatPsi.from(dotQualifiedExpression) ?: return
val assertThatArgument: KtValueArgument = assertThatPsi.getAssertThatArgument() ?: return
val isActivityFinishing = assertThatArgument.isActivityFinishing()
if (!isActivityFinishing) return
val assertThatExpression: KtReferenceExpression = assertThatPsi.getAssertThatExpression() ?: return
val isFalse = assertThatExpression.isFalse()
if (!isFalse) return
emit(
node.startOffset,
"Use utility function `Activity.assertIsNotFinishing()` to keep it consistent.",
false
)
}
Paul Dingemans
11/28/2023, 3:13 PMif (node.isAssertNotFinishingFunction()) {
//do not stop. Just skip this function.
stopTraversalOfAST()
return
}
with
if (node.isAssertNotFinishingFunction()) {
//do not stop. Just skip this function.
return
}
The beforeVisitChildNodes
is called for each individual node in the AST. Use the PsiViewer plugin to see how the AST looks like for your sample code (e.g. the sample that you use in your unit test). For each node you have to decide whether it needs to be processed, or that it needs to be skipped (does nothing and return without emit or altering the AST). The stopTraversalOfAST
is only meant for the case that you do not want to process the remainder of the AST Nodes.Ruben Quadros
11/29/2023, 7:33 AMfun Activity.isAssertNotFinishing() {
assertThat(isFinishing).isFalse
}
Now in beforeVisitChildNodes
i get every node.
For example one node is KtFuction
which is the function.. and the next node is PsiElement
which is "fun"
and next TypeReference
which is "Activity"
.
Since I already got a node which is KtFunction
I was assuming I would be able to skip the whole function but that is not the case.Ruben Quadros
11/29/2023, 9:36 AM@Test
fun `direct access to 'Activity_isFinishing()' is allowed inside 'Activity_assertIsNotFinishing()' extension method`() {
assertNoErrors(
"""
fun Activity.assertIsNotFinishing() {
assertThat(isFinishing).isFalse
}
""".trimIndent()
)
}
Paul Dingemans
11/29/2023, 3:05 PM