Hello! I am trying to write a custom RuleSet that ...
# detekt
e
Hello! I am trying to write a custom RuleSet that also does auto correction, but I am having a hard time getting things to work. First problem is that it seems like the
bindingContext
is
BindingContext.EMPTY
, which makes it impossible for me to check for the type of a
KtNameReferenceExpression
using
bindingContext.getType(receiverExpression)
. The second problem is when running tests, where I am overcoming the empty
BindingContext
issue by creating an environment using
io.github.detekt.test.utils.createEnvironment
, when I am trying to replace some code, I am getting an error:
Missing extension point: <http://org.jetbrains.kotlin.com|org.jetbrains.kotlin.com>.intellij.treeCopyHandler in container {}
(code snippet and full stack trace in thread)
Rule logic:
Copy code
private fun isMutableStateFlowValueAssignment(expression: KtBinaryExpression): Boolean {
        val leftSide = expression.left
        val rightSide = expression.right

        if (leftSide is KtDotQualifiedExpression) {
            val receiverExpression = leftSide.receiverExpression as? KtSimpleNameExpression ?: return false
            val selectorExpression = leftSide.selectorExpression as? KtNameReferenceExpression ?: return false
            val receiverType = bindingContext.getType(receiverExpression)

            if (receiverType?.unwrap()?.fqNameOrNull()?.asString() == "kotlinx.coroutines.flow.MutableStateFlow" && selectorExpression.getReferencedName() == "value") {
                withAutoCorrect {
                    val factory = KtPsiFactory(expression.project, markGenerated = false)
                    val replacement = factory.createExpression("${receiverExpression.text}.update { ${rightSide?.text} }") as KtDotQualifiedExpression
                    replacement.containingKtFile.analysisContext = expression.containingFile
                    val newImport = factory.createImportDirective(ImportPath.fromString("kotlinx.coroutines.flow.update"))

                    expression.containingKtFile.importList?.add(newImport)
                    expression.astReplace(replacement)
                }
                return true
            }
        }
        return false
    }
Stack trace:
Copy code
Missing extension point: org.jetbrains.kotlin.com.intellij.treeCopyHandler in container {}
java.lang.IllegalArgumentException: Missing extension point: org.jetbrains.kotlin.com.intellij.treeCopyHandler in container {}
	at org.jetbrains.kotlin.com.intellij.openapi.extensions.impl.ExtensionsAreaImpl.getExtensionPoint(ExtensionsAreaImpl.java:250)
	at org.jetbrains.kotlin.com.intellij.openapi.extensions.BaseExtensionPointName.getPointImpl(BaseExtensionPointName.java:28)
	at org.jetbrains.kotlin.com.intellij.openapi.extensions.ExtensionPointName.getExtensionList(ExtensionPointName.java:39)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.encodeInformation(ChangeUtil.java:43)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.lambda$encodeInformation$0(ChangeUtil.java:39)
	at org.jetbrains.kotlin.com.intellij.psi.impl.DebugUtil.performPsiModification(DebugUtil.java:481)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.encodeInformation(ChangeUtil.java:39)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.copyElement(ChangeUtil.java:95)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.copyElement(ChangeUtil.java:87)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.generateTreeElement(ChangeUtil.java:131)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.copyToElement(ChangeUtil.java:113)
	at org.jetbrains.kotlin.com.intellij.extapi.psi.ASTDelegatePsiElement.addInnerBefore(ASTDelegatePsiElement.java:278)
	at org.jetbrains.kotlin.com.intellij.extapi.psi.ASTDelegatePsiElement.add(ASTDelegatePsiElement.java:268)
	at com.wave.detekt.MutableStateFlowUpdate$isMutableStateFlowValueAssignment$1.invoke(MutableStateFlowUpdate.kt:69)
	at com.wave.detekt.MutableStateFlowUpdate$isMutableStateFlowValueAssignment$1.invoke(MutableStateFlowUpdate.kt:59)
	at io.gitlab.arturbosch.detekt.api.ConfigAware$DefaultImpls.withAutoCorrect(ConfigAware.kt:57)
	at io.gitlab.arturbosch.detekt.api.Rule.withAutoCorrect(Rule.kt:20)
	at com.wave.detekt.MutableStateFlowUpdate.isMutableStateFlowValueAssignment(MutableStateFlowUpdate.kt:59)
	at com.wave.detekt.MutableStateFlowUpdate.visitBinaryExpression(MutableStateFlowUpdate.kt:33)
Actually, if I comment out the line
expression.containingKtFile.importList?.add(newImport)
, I get a different error:
Copy code
ERROR: PSI invalidated outside transaction
java.lang.Throwable
	at org.jetbrains.kotlin.com.intellij.psi.impl.DebugUtil.handleUnspecifiedTrace(DebugUtil.java:539)
	at org.jetbrains.kotlin.com.intellij.psi.impl.DebugUtil.currentInvalidationTrace(DebugUtil.java:535)
	at org.jetbrains.kotlin.com.intellij.psi.impl.DebugUtil.calcInvalidationTrace(DebugUtil.java:530)
	at org.jetbrains.kotlin.com.intellij.psi.impl.DebugUtil.onInvalidated(DebugUtil.java:502)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.TreeElement.onInvalidated(TreeElement.java:227)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.TreeElement.rawRemoveUpToWithoutNotifications(TreeElement.java:401)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.TreeElement.rawRemoveUpTo(TreeElement.java:355)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement.remove(CompositeElement.java:812)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement.lambda$removeChildrenInner$4(CompositeElement.java:834)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil$1.runInner(ChangeUtil.java:147)
	at org.jetbrains.kotlin.com.intellij.pom.impl.PomTransactionBase.run(PomTransactionBase.java:28)
	at io.github.detekt.parser.DetektPomModel.runTransaction(DetektPomModel.kt:50)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.ChangeUtil.prepareAndRunChangeAction(ChangeUtil.java:142)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement.removeChildrenInner(CompositeElement.java:833)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement.replaceChild(CompositeElement.java:623)
	at org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt.astReplace(ktPsiUtil.kt:626)
	at com.wave.detekt.MutableStateFlowUpdate$isMutableStateFlowValueAssignment$1.invoke(MutableStateFlowUpdate.kt:70)
	at com.wave.detekt.MutableStateFlowUpdate$isMutableStateFlowValueAssignment$1.invoke(MutableStateFlowUpdate.kt:59)
	at io.gitlab.arturbosch.detekt.api.ConfigAware$DefaultImpls.withAutoCorrect(ConfigAware.kt:57)
	at io.gitlab.arturbosch.detekt.api.Rule.withAutoCorrect(Rule.kt:20)
	at com.wave.detekt.MutableStateFlowUpdate.isMutableStateFlowValueAssignment(MutableStateFlowUpdate.kt:59)
	at com.wave.detekt.MutableStateFlowUpdate.visitBinaryExpression(MutableStateFlowUpdate.kt:33)
Does anyone have any idea what might be going on?
From my debugging so far, it looks like the classpath in the
EnvironmentFacade
is empty, which causes
BindingContext.EMPTY
to be returned in
generateBindingContext
. But I can't really tell how the classpath should be populated and why it's not.
Small breakthrough: if I run
./gradlew detektMain
, the classpath is populated correctly, and my custom rule runs as expected. However, then the
--auto-correct
flag is not recognized.