Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing extension point: treeCopyHandler in container {} while using autoCorrect. #6666

Closed
TWiStErRob opened this issue Nov 30, 2023 · 7 comments
Labels

Comments

@TWiStErRob
Copy link
Member

TWiStErRob commented Nov 30, 2023

Expected Behavior

Test passes as before on 1.23.4.

Observed Behavior

java.lang.IllegalArgumentException: Missing extension point: org.jetbrains.kotlin.com.intellij.treeCopyHandler in container {} (click for full stack trace)
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.psi.impl.source.tree.SharedImplUtil.doReplace(SharedImplUtil.java:195)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement.replace(LeafPsiElement.java:198)
	at net.twisterrob.detekt.testing.rules.HodorRule.hodor(HodorRule.kt:44)
	at net.twisterrob.detekt.testing.rules.HodorRule.visitCallExpression(HodorRule.kt:32)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitCallExpression(KtVisitorVoid.java:811)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitCallExpression(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtCallExpression.accept(KtCallExpression.java:35)
	at org.jetbrains.kotlin.psi.KtElementImpl.accept(KtElementImpl.java:51)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LazyParseablePsiElement.acceptChildren(LazyParseablePsiElement.java:100)
	at org.jetbrains.kotlin.psi.KtTreeVisitorVoid.visitElement(KtTreeVisitorVoid.java:25)
	at org.jetbrains.kotlin.psi.KtVisitor.visitKtElement(KtVisitor.java:24)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:25)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:455)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitExpression(KtVisitor.java:186)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:173)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:667)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitBlockExpression(KtVisitor.java:318)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitBlockExpression(KtVisitorVoid.java:305)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitBlockExpression(KtVisitorVoid.java:865)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitBlockExpression(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtBlockExpression.accept(KtBlockExpression.java:79)
	at org.jetbrains.kotlin.psi.KtBlockExpression.accept(KtBlockExpression.java:86)
	at org.jetbrains.kotlin.com.intellij.psi.impl.PsiElementBase.acceptChildren(PsiElementBase.java:69)
	at org.jetbrains.kotlin.psi.KtTreeVisitorVoid.visitElement(KtTreeVisitorVoid.java:25)
	at org.jetbrains.kotlin.psi.KtVisitor.visitKtElement(KtVisitor.java:24)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:25)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:455)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtElement(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitExpression(KtVisitor.java:186)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:173)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:667)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitExpression(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitDeclaration(KtVisitor.java:29)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitDeclaration(KtVisitorVoid.java:29)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitDeclaration(KtVisitorVoid.java:461)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitDeclaration(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitNamedDeclaration(KtVisitor.java:406)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedDeclaration(KtVisitorVoid.java:385)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedDeclaration(KtVisitorVoid.java:973)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedDeclaration(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtVisitor.visitNamedFunction(KtVisitor.java:53)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedFunction(KtVisitorVoid.java:49)
	at net.twisterrob.detekt.testing.rules.HodorRule.visitNamedFunction(HodorRule.kt:26)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedFunction(KtVisitorVoid.java:491)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitNamedFunction(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtNamedFunction.accept(KtNamedFunction.java:49)
	at org.jetbrains.kotlin.psi.KtElementImplStub.accept(KtElementImplStub.java:49)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.SharedImplUtil.acceptChildren(SharedImplUtil.java:185)
	at org.jetbrains.kotlin.com.intellij.psi.impl.source.PsiFileImpl.acceptChildren(PsiFileImpl.java:754)
	at org.jetbrains.kotlin.psi.KtTreeVisitorVoid.visitElement(KtTreeVisitorVoid.java:25)
	at org.jetbrains.kotlin.com.intellij.psi.PsiElementVisitor.visitFile(PsiElementVisitor.java:35)
	at org.jetbrains.kotlin.psi.KtVisitor.visitKtFile(KtVisitor.java:73)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:69)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:521)
	at org.jetbrains.kotlin.psi.KtVisitorVoid.visitKtFile(KtVisitorVoid.java:21)
	at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:254)
	at org.jetbrains.kotlin.psi.KtFile.accept(KtFile.kt:241)
	at io.gitlab.arturbosch.detekt.api.BaseRule.visit(BaseRule.kt:52)
	at net.twisterrob.detekt.testing.FixKt.fix(fix.kt:46)
	at net.twisterrob.detekt.testing.FixTest$generic fix.returns modified code when rule supports autoCorrect(FixTest.kt:211)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)

Steps to Reproduce

I minimized this from my real problem branch.


val rule = HodorRule(TestConfig("autoCorrect" to true))
val ktFile = compileContentForTest(originalCode.trimIndent())
rule.visit(ktFile)

class HodorRule(config: Config = Config.empty) : Rule(config) {
  override fun visitCallExpression(expression: KtCallExpression) {
    super.visitCallExpression(expression)
    expression.getCallNameExpression()?.getIdentifier()?.let {
      report(CodeSmell(issue, Entity.from(it), "Hodor hodor"))
      if (autoCorrect) {
        it.replace(KtPsiFactory.contextual(it).createNameIdentifier("hodor"))
      }
    }
  }
}
// Click for full real and highlighted code.

import io.github.detekt.test.utils.compileContentForTest
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.test.TestConfig
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class MyTest {

	@Test
	fun `returns modified code when rule supports autoCorrect and autoCorrect is on`() {
		val originalCode = """
			fun main() {
				println()
			}
		""".trimIndent()

		@Language("kotlin")
		val expectedCode = """
			fun hodor() {
				hodor()
			}
		""".trimIndent()

		val rule = HodorRule(TestConfig("autoCorrect" to true))
		val ktFile = compileContentForTest(originalCode.trimIndent())
		rule.visit(ktFile)
		val fixedCode = ktFile.text

		assertEquals(fixedCode, expectedCode)
	}

	class HodorRule(config: Config = Config.empty) : Rule(config) {

		override val issue: Issue = Issue(
			id = "Hodor",
			description = "Hodor hodor, hodor.",
		)

		private val replacement: String by config("hodor")

		override fun visitNamedFunction(function: KtNamedFunction) {
			super.visitNamedFunction(function)
			function.nameIdentifier?.hodor()
		}

		override fun visitCallExpression(expression: KtCallExpression) {
			super.visitCallExpression(expression)
			expression.getCallNameExpression()?.getIdentifier()?.hodor()
		}

		private fun PsiElement.hodor() {
			report(CodeSmell(issue, Entity.from(this), "Hodor hodor"))
			if (autoCorrect) {
				replace(KtPsiFactory.contextual(this).createNameIdentifier(replacement))
			}
		}
	}
}

Context

This has been fixed once by @cortinico related to ktlint: #4545, but now I have a repro without, just directly use Detekt APIs. I do not understand this error at all :) I hope the fix won't be as evil as the number of this issue.

Potential red herring

There was a recent change while updating Kotlin detekt/Kotlin to latest, which might be relevant here:

-expression.replace(KtPsiFactory(expression).createStringTemplate(replacement))
+expression.replace(KtPsiFactory.contextual(expression).createStringTemplate(replacement))

I say red herring, because even with the old deprecated version it's failing. However I might be using the wrong API to generate replacement.

Your Environment

@TWiStErRob TWiStErRob added the bug label Nov 30, 2023
@TWiStErRob TWiStErRob changed the title Missing extension point: org.jetbrains.kotlin.com.intellij.treeCopyHandler in container {} while using autoCorrect. Missing extension point: treeCopyHandler in container {} while using autoCorrect. Nov 30, 2023
@cortinico
Copy link
Member

Test passes as before on 1.23.4.

Which tests are you referring to? The one on main?

@TWiStErRob
Copy link
Member Author

TWiStErRob commented Nov 30, 2023

No, my tests :) i.e. the one that's in the Repro. It works on 1.23.4 but not on current main. I think I've simplified it enough that it could be part of the detekt test suite.

@3flex
Copy link
Member

3flex commented Dec 2, 2023

Support for the extension was removed from ktlint as apparently it was removed from Kotlin from 1.9. There's more info on pinterest/ktlint#2044. It was removed from detekt as well in a different PR.

I don't think this is something we can do much about and the rules will need to be adjusted to avoid hitting methods that require the extension to be supported. More info on that is in the linked issue.

@TWiStErRob
Copy link
Member Author

Ah, nice, so I see this is just straight up removed in #6255.
Thanks for the link, that's a nice resource. I'll try to adjust my rule then.

@TWiStErRob
Copy link
Member Author

I wonder if we should provide a ruleauthors rule that flags affected methods (even if the list is non-exhaustive), because all of those methods look like normal public API, they just break at runtime. This could even help ktlint rule authors (if they have detekt set up on the project).

@3flex
Copy link
Member

3flex commented Dec 3, 2023

It's a thought, however autocorrect isn't something we really support at all outside of the ktlint wrapper so I think it would be better as a third party rule personally.

I'll close this issue though as it's something that we won't/can't fix in detekt.

@3flex 3flex closed this as not planned Won't fix, can't repro, duplicate, stale Dec 3, 2023
@TWiStErRob
Copy link
Member Author

If anyone needs it, here how I resolve this exception: TWiStErRob/net.twisterrob.detekt@389baa8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants