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