diff --git a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredService.kt b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredService.kt index 2ce265d..171053b 100644 --- a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredService.kt +++ b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredService.kt @@ -13,12 +13,14 @@ import io.github.detekt.tooling.api.spec.RulesSpec import io.gitlab.arturbosch.detekt.api.Finding import io.gitlab.arturbosch.detekt.api.UnstableApi import io.gitlab.arturbosch.detekt.idea.config.DetektConfigStorage -import io.gitlab.arturbosch.detekt.idea.util.DirectExecuter +import io.gitlab.arturbosch.detekt.idea.util.DirectExecutor +import io.gitlab.arturbosch.detekt.idea.util.PluginUtils import io.gitlab.arturbosch.detekt.idea.util.absolutePath import io.gitlab.arturbosch.detekt.idea.util.extractPaths import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import java.util.ServiceLoader class ConfiguredService(private val project: Project) { @@ -70,7 +72,7 @@ class ConfiguredService(private val project: Project) { } } execution { - executorService = DirectExecuter() + executorService = DirectExecutor() } } @@ -97,7 +99,7 @@ class ConfiguredService(private val project: Project) { } val spec: ProcessingSpec = settings(filename, autoCorrect) - val detekt = DetektProvider.load().get(spec) + val detekt = loadProviderConsiderStubbed().get(spec) val result = if (autoCorrect) { runWriteAction { detekt.run(fileContent, filename) } } else { @@ -113,3 +115,10 @@ class ConfiguredService(private val project: Project) { return result.container?.findings?.flatMap { it.value } ?: emptyList() } } + +fun loadProviderConsiderStubbed(): DetektProvider { + val providers = ServiceLoader.load(DetektProvider::class.java, PluginUtils::class.java.classLoader).toList() + return providers + .find { it.javaClass.simpleName == "StubDetektProvider" } + ?: providers.first() +} diff --git a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotator.kt b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotator.kt index 54be3b2..55b0986 100644 --- a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotator.kt +++ b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotator.kt @@ -11,8 +11,8 @@ import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.idea.config.DetektConfigStorage import io.gitlab.arturbosch.detekt.idea.intention.AutoCorrectIntention import io.gitlab.arturbosch.detekt.idea.util.isDetektEnabled -import io.gitlab.arturbosch.detekt.idea.util.isKotlinFile import io.gitlab.arturbosch.detekt.idea.util.showNotification +import org.jetbrains.kotlin.idea.KotlinLanguage class DetektAnnotator : ExternalAnnotator>() { @@ -22,7 +22,7 @@ class DetektAnnotator : ExternalAnnotator>() { if (!collectedInfo.project.isDetektEnabled()) { return emptyList() } - if (collectedInfo.isKotlinFile()) { + if (collectedInfo.language.id != KotlinLanguage.INSTANCE.id) { return emptyList() } diff --git a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/config/DetektConfigStorage.kt b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/config/DetektConfigStorage.kt index 86939d1..a4e5c09 100644 --- a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/config/DetektConfigStorage.kt +++ b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/config/DetektConfigStorage.kt @@ -1,7 +1,6 @@ package io.gitlab.arturbosch.detekt.idea.config import com.intellij.openapi.components.PersistentStateComponent -import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.project.Project diff --git a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecuter.kt b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecutor.kt similarity index 91% rename from src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecuter.kt rename to src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecutor.kt index ae4efe0..fb89a0f 100644 --- a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecuter.kt +++ b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/DirectExecutor.kt @@ -3,7 +3,7 @@ package io.gitlab.arturbosch.detekt.idea.util import java.util.concurrent.AbstractExecutorService import java.util.concurrent.TimeUnit -class DirectExecuter : AbstractExecutorService() { +class DirectExecutor : AbstractExecutorService() { override fun isTerminated(): Boolean = true diff --git a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/ProjectUtils.kt b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/ProjectUtils.kt index 378b093..55cce0b 100644 --- a/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/ProjectUtils.kt +++ b/src/main/kotlin/io/gitlab/arturbosch/detekt/idea/util/ProjectUtils.kt @@ -7,11 +7,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.options.newEditor.SettingsDialog import com.intellij.openapi.project.Project -import com.intellij.psi.PsiFile import io.gitlab.arturbosch.detekt.idea.DETEKT import io.gitlab.arturbosch.detekt.idea.config.DetektConfig import io.gitlab.arturbosch.detekt.idea.config.DetektConfigStorage -import org.jetbrains.kotlin.idea.KotlinLanguage import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -19,8 +17,6 @@ import java.nio.file.Paths fun Project.isDetektEnabled(): Boolean = DetektConfigStorage.instance(this).enableDetekt -fun PsiFile.isKotlinFile(): Boolean = language == KotlinLanguage.INSTANCE - fun absolutePath(project: Project, path: String): String = if (path.isBlank() || File(path).isAbsolute) { path diff --git a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredServiceTest.kt b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredServiceTest.kt index b7c518e..df76725 100644 --- a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredServiceTest.kt +++ b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/ConfiguredServiceTest.kt @@ -4,7 +4,6 @@ import io.github.detekt.test.utils.readResourceContent import io.github.detekt.test.utils.resourceAsPath import io.gitlab.arturbosch.detekt.idea.config.DetektConfigStorage import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.Test class ConfiguredServiceTest : DetektPluginTestCase() { @@ -58,22 +57,13 @@ class ConfiguredServiceTest : DetektPluginTestCase() { fun `expect detekt runs successfully`() { val service = ConfiguredService(project) - // If a NoSuchMethodError is thrown, it means detekt-core was called and creating a KotlinEnvironment - // failed due to conflicted compiler vs embedded-compiler dependency. - // IntelliJ isolates plugins in an own classloader so detekt runs fine. - // In the testcase this is not possible but it is enough to prove detekt runs and does not crash due to - // regressions in this plugin. - assertThatCode { + assertThat( service.execute( readResourceContent("testData/Poko.kt"), resourceAsPath("testData/Poko.kt").toString(), autoCorrect = false ) - } - .isInstanceOf(ClassCastException::class.java) - .hasMessageContaining( - "class org.jetbrains.kotlin.idea.KotlinFileType cannot be cast" - ) + ).isNotEmpty } @Test diff --git a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotatorTest.kt b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotatorTest.kt index eac8743..6512bfb 100644 --- a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotatorTest.kt +++ b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektAnnotatorTest.kt @@ -2,32 +2,27 @@ package io.gitlab.arturbosch.detekt.idea import com.intellij.openapi.application.WriteAction import com.intellij.psi.PsiFile +import io.gitlab.arturbosch.detekt.api.Finding import io.gitlab.arturbosch.detekt.idea.config.DetektConfigStorage import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class DetektAnnotatorTest : DetektPluginTestCase() { @Test fun `returns empty list when detekt is disabled`() { - val file = myFixture.copyFileToProject("Poko.kt") - val psi = WriteAction.computeAndWait { psiManager.findFile(file)!! } - - val findings = DetektAnnotator().doAnnotate(psi) - - assertThat(findings).isEmpty() + assertThat(runAnnotator(enabled = false)).isEmpty() } @Test - @Disabled("Dependency conflicts between embeddable and normal kotlin compiler.") fun `report issues when detekt is enabled`() { - DetektConfigStorage.instance(project).enableDetekt = true + assertThat(runAnnotator(enabled = true)).isNotEmpty + } + + private fun runAnnotator(enabled: Boolean): List { + DetektConfigStorage.instance(project).enableDetekt = enabled val file = myFixture.copyFileToProject("Poko.kt") val psi = WriteAction.computeAndWait { psiManager.findFile(file)!! } - - val findings = DetektAnnotator().doAnnotate(psi) - - assertThat(findings).isNotEmpty() + return DetektAnnotator().doAnnotate(psi) } } diff --git a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektPluginTestCase.kt b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektPluginTestCase.kt index e80719d..f5a217e 100644 --- a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektPluginTestCase.kt +++ b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektPluginTestCase.kt @@ -15,6 +15,12 @@ import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) open class DetektPluginTestCase : BasePlatformTestCase() { + init { + // necessary in tests up from 2021.3 + // https://plugins.jetbrains.com/docs/intellij/api-changes-list-2021.html#20213 + System.setProperty("idea.force.use.core.classloader", "true") + } + override fun getTestDataPath(): String = resourceAsPath("testData").toString() override fun isWriteActionRequired(): Boolean = true diff --git a/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektStub.kt b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektStub.kt new file mode 100644 index 0000000..559cf18 --- /dev/null +++ b/src/test/kotlin/io/gitlab/arturbosch/detekt/idea/DetektStub.kt @@ -0,0 +1,95 @@ +package io.gitlab.arturbosch.detekt.idea + +import io.github.detekt.psi.FilePath +import io.github.detekt.tooling.api.AnalysisResult +import io.github.detekt.tooling.api.Detekt +import io.github.detekt.tooling.api.DetektProvider +import io.github.detekt.tooling.api.spec.ProcessingSpec +import io.github.detekt.tooling.internal.DefaultAnalysisResult +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Detektion +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Finding +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Location +import io.gitlab.arturbosch.detekt.api.Notification +import io.gitlab.arturbosch.detekt.api.ProjectMetric +import io.gitlab.arturbosch.detekt.api.RuleSetId +import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.SourceLocation +import io.gitlab.arturbosch.detekt.api.TextLocation +import org.jetbrains.kotlin.com.intellij.openapi.util.Key +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext +import java.nio.file.Path +import java.nio.file.Paths + +class DetektProviderStub : DetektProvider { + + override fun get(processingSpec: ProcessingSpec): Detekt = DetektStub() +} + +class DetektStub : Detekt { + + override fun run(): AnalysisResult { + throw UnsupportedOperationException() + } + + override fun run(path: Path): AnalysisResult { + throw UnsupportedOperationException() + } + + override fun run(sourceCode: String, filename: String): AnalysisResult { + if (!filename.contains("Poko.kt")) { + throw UnsupportedOperationException("Only Poko.kt runs are supported.") + } + return DefaultAnalysisResult(object : Detektion { + override val findings: Map> = mapOf( + "empty-blocks" to listOf( + CodeSmell( + Issue( + "EmptyDefaultConstructor", + Severity.Minor, + "empty", + Debt.FIVE_MINS + ), + Entity( + "Poko", + "testData.Poko.kt", + Location( + SourceLocation(3, 10), + TextLocation(28, 30), + FilePath(Paths.get(filename)) + ) + ), + "empty constructor" + ) + ) + ) + override val metrics: Collection = emptyList() + override val notifications: Collection = emptyList() + + override fun add(notification: Notification) { + // ignore + } + + override fun add(projectMetric: ProjectMetric) { + // ignore + } + + override fun addData(key: Key, value: V) { + // ignore + } + + override fun getData(key: Key): V? { + // ignore + return null + } + }) + } + + override fun run(files: Collection, bindingContext: BindingContext): AnalysisResult { + throw UnsupportedOperationException() + } +} diff --git a/src/test/resources/META-INF/services/io.github.detekt.tooling.api.DetektProvider b/src/test/resources/META-INF/services/io.github.detekt.tooling.api.DetektProvider new file mode 100644 index 0000000..2278bd4 --- /dev/null +++ b/src/test/resources/META-INF/services/io.github.detekt.tooling.api.DetektProvider @@ -0,0 +1 @@ +io.gitlab.arturbosch.detekt.idea.DetektProviderStub