Are there compiler APIs I can use to incrementally...
# compiler
j
Are there compiler APIs I can use to incrementally analyze files ? This is how I am analyzing Kotlin code atm, but except keeping the KotlinCoreEnvironment between analysis I don't do any caching/incremental analysis. Any idea how to achieve that ?
Copy code
import com.intellij.openapi.Disposable
import com.intellij.psi.PsiFileFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.compiler.CliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import kotlin.reflect.jvm.internal.impl.storage.StorageManager

class Analyzer {
  private val compilerConfiguration = CompilerConfiguration().apply {
    put(CommonConfigurationKeys.MODULE_NAME, JvmProtoBufUtil.DEFAULT_MODULE_NAME)
    put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, StdOutMessageCollector)
    put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
  }

  private val kotlinEnvironment = KotlinCoreEnvironment.createForProduction(
    parentDisposable = Disposable { },
    configuration = compilerConfiguration,
    configFiles = EnvironmentConfigFiles.JVM_CONFIG_FILES
  )

  private val project = kotlinEnvironment.project
  private val psiFactory = PsiFileFactory.getInstance(project)

  fun analyzeCode(code: String): Pair<KtFile, BindingContext> {
    val file = createKotlinFile(code)
    return file to analyze(file)
  }

  private fun createKotlinFile(code: String): KtFile {
    val file = psiFactory.createFileFromText("Dumb.kt", KotlinLanguage.INSTANCE, code, true, false)
    return file as KtFile
  }

  private fun analyze(psiFile: KtFile): BindingContext {
    val files = arrayListOf(psiFile)

    val trace = CliBindingTrace()
    val container = TopDownAnalyzerFacadeForJVM.createContainer(
      project,
      emptyList(),
      trace,
      kotlinEnvironment.configuration,
      kotlinEnvironment::createPackagePartProvider,
      { storageManager, _ -> FileBasedDeclarationProviderFactory(storageManager, files) }
    )

    val analyzer = container.get<LazyTopDownAnalyzer>()
    analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
    return trace.bindingContext
  }
}
t
Perhaps it would be easier to call the entire compiler and do your analysis in a compiler plugin? You can do that in-process using my compile-testing library. It uses the regular compiler so all the incremental stuff is there in theory, although I don't know how Gradle keeps the compiler alive or where the incremental data is cached. If you're lucky it's enough to just keep the build directory around inbetween compilations.