Repository: detekt/detekt (kotlin)

Issue:
Add supporting infrastructure to allow usage of `analyze` entry point in rules
## Expected Behavior
detekt-parser creates a Standalone Analysis API session that can be used by rules that require support for semantic analysis.

## Current Behavior
Semantic analysis is supported through generation of a `BindingContext`.

## Context
Required for migration to Analysis API.

Pull request:
Closes #8047

Patch:
diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/settings/EnvironmentAware.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/settings/EnvironmentAware.kt
index d3990b7a5fae..5e8024eac0d3 100644
--- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/settings/EnvironmentAware.kt
+++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/settings/EnvironmentAware.kt
@@ -1,5 +1,7 @@
 package io.gitlab.arturbosch.detekt.core.settings
 
+import com.google.devtools.ksp.standalone.buildKspLibraryModule
+import com.google.devtools.ksp.standalone.buildKspSdkModule
 import com.intellij.openapi.Disposable
 import com.intellij.openapi.project.Project
 import com.intellij.openapi.util.Disposer
@@ -9,22 +11,36 @@ import io.github.detekt.parser.createCompilerConfiguration
 import io.github.detekt.tooling.api.spec.CompilerSpec
 import io.github.detekt.tooling.api.spec.LoggingSpec
 import io.github.detekt.tooling.api.spec.ProjectSpec
+import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
+import org.jetbrains.kotlin.analysis.api.projectStructure.KaSourceModule
 import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession
+import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule
 import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl
+import org.jetbrains.kotlin.cli.common.config.kotlinSourceRoots
 import org.jetbrains.kotlin.cli.jvm.compiler.CliTraceHolder
+import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
 import org.jetbrains.kotlin.config.CommonConfigurationKeys
 import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.config.JvmTarget
 import org.jetbrains.kotlin.config.friendPaths
+import org.jetbrains.kotlin.config.jdkHome
+import org.jetbrains.kotlin.config.jvmTarget
+import org.jetbrains.kotlin.config.languageVersionSettings
 import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
+import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
+import org.jetbrains.kotlin.psi.KtFile
 import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer
 import java.io.Closeable
 import java.io.File
 import java.io.OutputStream
 import java.io.PrintStream
+import java.util.UUID
+import kotlin.io.path.Path
 
 interface EnvironmentAware {
     val project: Project
     val configuration: CompilerConfiguration
+    val ktFiles: List<KtFile>
     val disposable: Disposable
 }
 
@@ -49,6 +65,12 @@ internal class EnvironmentFacade(
 
     override val disposable: Disposable = Disposer.newDisposable()
 
+    private lateinit var sourceModule: KaSourceModule
+
+    @OptIn(KaExperimentalApi::class)
+    override val ktFiles: List<KtFile>
+        get() = sourceModule.psiRoots.filterIsInstance<KtFile>()
+
     private val analysisSession = buildStandaloneAnalysisAPISession(disposable) {
         // Required for autocorrect support
         registerProjectService(PomModel::class.java, DetektPomModel)
@@ -61,8 +83,55 @@ internal class EnvironmentFacade(
 
         configuration.putIfAbsent(CommonConfigurationKeys.MODULE_NAME, "<no module name provided>")
 
-        @Suppress("DEPRECATION") // Required until fully transitioned to setting up Kotlin Analysis API session
-        buildKtModuleProviderByCompilerConfiguration(configuration)
+        buildKtModuleProvider {
+            val targetPlatform =
+                JvmPlatforms.jvmPlatformByTargetVersion(configuration.jvmTarget ?: JvmTarget.DEFAULT)
+            platform = targetPlatform
+
+            val jdk = configuration.jdkHome?.let { jdkHome ->
+                buildKspSdkModule {
+                    addBinaryRootsFromJdkHome(jdkHome.toPath(), isJre = false)
+                    platform = targetPlatform
+                    libraryName = "jdk"
+                }
+            }
+
+            val friends = configuration.friendPaths.map {
+                buildKspLibraryModule {
+                    platform = targetPlatform
+                    addBinaryRoot(Path(it))
+                    libraryName = UUID.randomUUID().toString()
+                }
+            }
+
+            val dependencies = configuration.jvmClasspathRoots.map {
+                buildKspLibraryModule {
+                    platform = targetPlatform
+                    addBinaryRoot(it.toPath())
+                    libraryName = "regulardependencies"
+                }
+            }
+
+            sourceModule = buildKtSourceModule {
+                addSourceRoots(configuration.kotlinSourceRoots.map { Path(it.path) })
+                platform = targetPlatform
+                moduleName = "source"
+
+                jdk?.let { addRegularDependency(it) }
+                friends.forEach {
+                    // Friend dependencies must also be declared as regular dependencies - https://github.com/JetBrains/kotlin/commit/69cfa0498a76f0c3eec39eb06b5de70a0d06e41a
+                    addFriendDependency(it)
+                    addRegularDependency(it)
+                }
+                dependencies.forEach {
+                    addRegularDependency(it)
+                }
+
+                languageVersionSettings = configuration.languageVersionSettings
+            }
+
+            addModule(sourceModule)
+        }
     }
 
     override val project: Project by lazy {
diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/AnalysisFacade.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/AnalysisFacade.kt
index f53949bbc7b5..491109726413 100644
--- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/AnalysisFacade.kt
+++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/AnalysisFacade.kt
@@ -19,7 +19,7 @@ class AnalysisFacade(
 ) : Detekt {
 
     override fun run(): AnalysisResult = runAnalysis {
-        DefaultLifecycle(spec.getDefaultConfiguration(), it, inputPathsToKtFiles)
+        DefaultLifecycle(spec.getDefaultConfiguration(), it)
     }
 
     override fun run(files: Collection<KtFile>, bindingContext: BindingContext): AnalysisResult =
@@ -27,7 +27,6 @@ class AnalysisFacade(
             DefaultLifecycle(
                 spec.getDefaultConfiguration(),
                 it,
-                parsingStrategy = { files.toList() },
                 bindingProvider = { bindingContext }
             )
         }
diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
index 7bb19c684b7d..6dd710f0fec0 100644
--- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
+++ b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/Lifecycle.kt
@@ -31,7 +31,6 @@ internal interface Lifecycle {
 
     val baselineConfig: Config
     val settings: ProcessingSettings
-    val parsingStrategy: ParsingStrategy
     val bindingProvider: (files: List<KtFile>) -> BindingContext
     val processorsProvider: () -> List<FileProcessListener>
     val ruleSetsProvider: () -> List<RuleSetProvider>
@@ -40,7 +39,7 @@ internal interface Lifecycle {
 
     fun analyze(): Detektion {
         measure(Phase.ValidateConfig) { checkConfiguration(settings, baselineConfig) }
-        val filesToAnalyze = measure(Phase.Parsing) { parsingStrategy.invoke(settings) }
+        val filesToAnalyze = measure(Phase.Parsing) { settings.ktFiles }
         val bindingContext = measure(Phase.Binding) { bindingProvider.invoke(filesToAnalyze) }
         val (processors, rules) = measure(Phase.LoadingExtensions) {
             val rules = getRules(
@@ -72,7 +71,6 @@ internal interface Lifecycle {
 internal class DefaultLifecycle(
     override val baselineConfig: Config,
     override val settings: ProcessingSettings,
-    override val parsingStrategy: ParsingStrategy,
     override val bindingProvider: (files: List<KtFile>) -> BindingContext =
         {
             if (settings.spec.projectSpec.analysisMode == AnalysisMode.full) {
diff --git a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/ParsingStrategy.kt b/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/ParsingStrategy.kt
deleted file mode 100644
index 2ea8cbf0ae6b..000000000000
--- a/detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/tooling/ParsingStrategy.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.gitlab.arturbosch.detekt.core.tooling
-
-import io.github.detekt.parser.KtCompiler
-import io.gitlab.arturbosch.detekt.core.ProcessingSettings
-import org.jetbrains.kotlin.psi.KtFile
-
-typealias ParsingStrategy = (settings: ProcessingSettings) -> List<KtFile>
-
-val inputPathsToKtFiles: ParsingStrategy = { settings ->
-    val compiler = KtCompiler(settings.project)
-    settings.spec.projectSpec.inputPaths.map { path ->
-        compiler.compile(path)
-    }
-}


Test patch:
diff --git a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/TopLevelAutoCorrectSpec.kt b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/TopLevelAutoCorrectSpec.kt
index e32351efd225..2b4acbc49043 100644
--- a/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/TopLevelAutoCorrectSpec.kt
+++ b/detekt-core/src/test/kotlin/io/gitlab/arturbosch/detekt/core/TopLevelAutoCorrectSpec.kt
@@ -13,7 +13,6 @@ import io.gitlab.arturbosch.detekt.api.RuleSet
 import io.gitlab.arturbosch.detekt.api.RuleSetProvider
 import io.gitlab.arturbosch.detekt.core.tooling.AnalysisFacade
 import io.gitlab.arturbosch.detekt.core.tooling.DefaultLifecycle
-import io.gitlab.arturbosch.detekt.core.tooling.inputPathsToKtFiles
 import org.assertj.core.api.Assertions.assertThat
 import org.jetbrains.kotlin.psi.KtAnnotation
 import org.jetbrains.kotlin.psi.KtFile
@@ -54,7 +53,6 @@ class TopLevelAutoCorrectSpec {
             DefaultLifecycle(
                 settings.config,
                 settings,
-                inputPathsToKtFiles,
                 processorsProvider = { listOf(contentChangedListener) },
                 ruleSetsProvider = { listOf(TopLevelAutoCorrectProvider()) }
             )