[Analysis API] implement API to extend Kotlin resolution by generated declarations

^KT-57930 fixed
This commit is contained in:
Ilya Kirillov
2023-04-17 18:12:00 +02:00
committed by Space Team
parent 0dbc948616
commit d68587de77
12 changed files with 571 additions and 14 deletions
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.resolve.extensions
import com.intellij.openapi.util.ModificationTracker
import org.jetbrains.kotlin.name.FqName
/**
* Provides a list of Kotlin files which provides additional, generated declarations for resolution.
*
* Provided by the [KtResolveExtensionProvider].
*
* All member implementations should:
* - consider caching the results for subsequent invocations.
* - be lightweight and not build the whole file structure inside.
* - not use the Kotlin resolve inside, as this function is called during session initialization, so Analysis API access is forbidden.
*
* @see KtResolveExtensionFile
* @see KtResolveExtensionProvider
*/
abstract class KtResolveExtension {
/**
* Get the list of files that should be generated for the module.
*
* The content of those files should remain valid until the tracker [getModificationTracker] is modified.
*
* Returned files should contain a valid Kotlin code.
*
* @see KtResolveExtensionFile
* @see KtResolveExtension
*/
abstract fun getKtFiles(): List<KtResolveExtensionFile>
/**
* Returns a [ModificationTracker], which controls the validity lifecycle of the files provided by [getKtFiles].
*
* All files generated by [getKtFiles] should be valid if the [ModificationTracker] did not change.
* If files become invalid (e.g., the in-source declarations they were based on changed) the [ModificationTracker] should be incremented.
*
* @see KtResolveExtension
*/
abstract fun getModificationTracker(): ModificationTracker
/**
* Returns the set of packages that are contained in the files provided by [getKtFiles].
*
* The returned package set should be a strict set of all file packages,
* so `for-all pckg: pckg in getContainedPackages() <=> exists file: file in getKtFiles() && file.getFilePackageName() == pckg`
*
* @see KtResolveExtension
*/
abstract fun getContainedPackages(): Set<FqName>
}
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.resolve.extensions
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
/**
* Represents the Kotlin file which provides additional, generated declarations for resolution.
*
* All member implementations should:
* - consider caching the results for subsequent invocations.
* - be lightweight and not build the whole file structure inside.
* - not use the Kotlin resolve inside, as this function is called during session initialization, so Analysis API access is forbidden.
*
* @see KtResolveExtension
*/
abstract class KtResolveExtensionFile {
/**
* The name a Kotlin file which will be generated.
*
* Should have the `.kt` extension.
*
* It will be used as a Java facade name, e.g., for the file name `myFile.kt`, the `MyFileKt` facade is generated if the file contains some properties or functions.
*
* @see KtResolveExtensionFile
*/
abstract fun getFileName(): String
/**
* [FqName] of the package specified in the file
*
* The operation might be called regularly, so the [getFilePackageName] should work fast and avoid building the whole file text.
*
* It should be equal to the package name specified in the [buildFileText].
*
* @see KtResolveExtensionFile
*/
abstract fun getFilePackageName(): FqName
/**
* Returns the set of top-level classifier (classes, interfaces, objects, and type-aliases) names in the file.
*
* The result may have false-positive entries but cannot have false-negative entries. It should contain all the names in the package but may have some additional names that are not there.
*
* @see KtResolveExtensionFile
*/
abstract fun getTopLevelClassifierNames(): Set<Name>
/**
* Returns the set of top-level callable (functions and properties) names in the file.
*
* The result may have false-positive entries but cannot have false-negative entries. It should contain all the names in the package but may have some additional names that are not there.
*
* @see KtResolveExtensionFile
*/
abstract fun getTopLevelCallableNames(): Set<Name>
/**
* Creates the generated Kotlin source file text.
*
* The resulted String should be a valid Kotlin code.
* It should be consistent with other declarations which are present in the [KtResolveExtensionFile], more specifically:
* 1. [getFilePackageName] should be equal to the file's package name.
* 2. All classifier names should be contained in the [getTopLevelClassifierNames].
* 3. All callable names should be contained in the [getTopLevelCallableNames].
*
* Additional restrictions on the file text:
* 1. The File should not contain the `kotlin.jvm.JvmMultifileClass` and `kotlin.jvm.JvmName` annotations on the file level.
* 2. All declaration types should be specified explicitly.
*
* @see KtResolveExtensionFile
*/
abstract fun buildFileText(): String
/**
* Creates a [KtResolveExtensionReferenceTargetsPsiProvider] for this [KtResolveExtensionFile].
*
* @see KtResolveExtensionReferenceTargetsPsiProvider
* @see KtResolveExtensionFile
*/
abstract fun createTargetPsiProvider(): KtResolveExtensionReferenceTargetsPsiProvider
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.analysis.api.resolve.extensions
import com.intellij.openapi.extensions.ExtensionPointName
import org.jetbrains.kotlin.analysis.project.structure.KtModule
/**
* Allows extending Kotlin resolution by generating additional declarations.
*
* Those declarations will be analyzed the same way as they were just regular source files inside the project.
*
* All member implementations should consider caching the results for subsequent invocations.
*/
abstract class KtResolveExtensionProvider {
/**
* Provides a list of [KtResolveExtension]s for a given [KtModule].
*
* Should not perform any heavy analysis and the generation of the actual files. All file generation should be performed only in [KtResolveExtensionFile.buildFileText].
*
* Implementations should consider caching the results, so the subsequent invocations should be performed instantly.
*
* Implementation cannot use the Kotlin resolve inside, as this function is called during session initialization, so Analysis API access is forbidden.
*/
abstract fun provideExtensionsFor(module: KtModule): List<KtResolveExtension>
companion object {
val EP_NAME = ExtensionPointName<KtResolveExtensionProvider>("org.jetbrains.kotlin.ktResolveExtensionProvider")
fun provideExtensionsFor(module: KtModule): List<KtResolveExtension> {
return EP_NAME.getExtensionList(module.project).flatMap { it.provideExtensionsFor(module) }
}
}
}