[Analysis API] Support non-JVM platforms in code fragments

The 'evalJs' test is not available, as there is no proper support for
klib loading in non-standalone Analysis API yet.

^KT-64197 Fixed
This commit is contained in:
Yan Zhulanow
2023-12-22 15:49:10 +09:00
committed by Space Team
parent 75abae23d8
commit 5179462632
20 changed files with 212 additions and 23 deletions
@@ -85,6 +85,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/annotationUsage.kt");
}
@Test
@TestMetadata("commonContext.kt")
public void testCommonContext() throws Exception {
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/commonContext.kt");
}
@Test
@TestMetadata("customName.kt")
public void testCustomName() throws Exception {
@@ -85,6 +85,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/annotationUsage.kt");
}
@Test
@TestMetadata("commonContext.kt")
public void testCommonContext() throws Exception {
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/commonContext.kt");
}
@Test
@TestMetadata("customName.kt")
public void testCustomName() throws Exception {
@@ -46,6 +46,18 @@ public class FirIdeNormalAnalysisSourceModuleCodeFragmentCollectDiagnosticsTestG
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("evalJvm.kt")
public void testEvalJvm() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/evalJvm.kt");
}
@Test
@TestMetadata("evalJvmJs.kt")
public void testEvalJvmJs() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/evalJvmJs.kt");
}
@Test
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
@@ -46,6 +46,18 @@ public class FirStandaloneNormalAnalysisSourceModuleCodeFragmentCollectDiagnosti
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("evalJvm.kt")
public void testEvalJvm() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/evalJvm.kt");
}
@Test
@TestMetadata("evalJvmJs.kt")
public void testEvalJvmJs() throws Exception {
runTest("analysis/analysis-api/testData/components/diagnosticsProvider/codeFragmentDiagnostics/evalJvmJs.kt");
}
@Test
@TestMetadata("localClass.kt")
public void testLocalClass() throws Exception {
@@ -0,0 +1,13 @@
MODULE_FRAGMENT
FILE fqName:<root> fileName:fragment.kt
CLASS CLASS name:CodeFragment modality:FINAL visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.CodeFragment
CONSTRUCTOR visibility:public <> () returnType:<root>.CodeFragment [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
FUN name:run visibility:public modality:FINAL <> (p0:test.Foo) returnType:kotlin.Unit
VALUE_PARAMETER name:p0 index:0 type:test.Foo
EXPRESSION_BODY
BLOCK type=kotlin.Unit origin=null
CALL 'public final fun call (): kotlin.Unit [expect] declared in test.Foo' type=kotlin.Unit origin=null
$this: GET_VAR 'p0: test.Foo declared in <root>.CodeFragment.run' type=test.Foo origin=null
@@ -0,0 +1,23 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: common
// TARGET_PLATFORM: Common
// FILE: Common.kt
package test
fun test(foo: Foo) {
<caret>foo.call()
}
expect class Foo {
fun call()
}
// MODULE: main()()(common)
// TARGET_PLATFORM: JVM
// FILE: Jvm.kt
package test
actual class Foo {
fun call() {}
}
@@ -0,0 +1,5 @@
public final class CodeFragment {
// source: 'fragment.kt'
public method <init>(): void
public final static method run(p0: test.Foo): void
}
@@ -0,0 +1,6 @@
// WITH_STDLIB
// TARGET_PLATFORM: JVM
fun foo(node: kotlinx.dom.Node) {
<caret>check(node.isElement)
}
@@ -0,0 +1,4 @@
Diagnostics from elements:
for PSI element of type KtNameReferenceExpression at (1,1-5)
UNRESOLVED_REFERENCE text ranges: [(0,4)]
PSI: KtNameReferenceExpression at (1,1-5)
@@ -0,0 +1,6 @@
// WITH_STDLIB
// TARGET_PLATFORM: JVM, JS
fun foo(node: kotlinx.dom.Node) {
<caret>check(node.isElement)
}
@@ -0,0 +1,4 @@
Diagnostics from elements:
for PSI element of type KtNameReferenceExpression at (1,1-5)
UNRESOLVED_REFERENCE text ranges: [(0,4)]
PSI: KtNameReferenceExpression at (1,1-5)
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.compiledLibraryProvider
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.platform.isMultiPlatform
import org.junit.Assume
import java.nio.file.Path
/**
@@ -21,6 +23,8 @@ import java.nio.file.Path
*/
object KtLibrarySourceModuleFactory : KtModuleFactory {
override fun createModule(testModule: TestModule, testServices: TestServices, project: Project): KtModuleWithFiles {
Assume.assumeFalse("Compilation of multi-platform libraries is not supported", testModule.targetPlatform.isMultiPlatform())
val (libraryJar, librarySourcesJar) = testServices.compiledLibraryProvider.compileToLibrary(testModule)
require(librarySourcesJar != null)
@@ -461,10 +461,14 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
}
}
fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession {
val platform = module.platform
require(platform.has<JvmPlatform>())
abstract fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession
protected fun doCreateCodeFragmentSession(
module: KtCodeFragmentModule,
contextSession: LLFirSession,
additionalSessionConfiguration: context(CodeFragmentSessionCreationContext) LLFirCodeFragmentSession.() -> Unit,
): LLFirSession {
val platform = module.platform
val builtinsSession = LLFirBuiltinsSessionFactory.getInstance(project).getBuiltinsSession(platform)
val languageVersionSettings = wrapLanguageVersionSettings(contextSession.languageVersionSettings)
val scopeProvider = FirKotlinScopeProvider(::wrapScopeWithJvmMapped)
@@ -515,34 +519,33 @@ internal abstract class LLFirAbstractSessionFactory(protected val project: Proje
extensionService.additionalCheckers.forEach(session.checkersComponent::register)
register(FirPredicateBasedProvider::class, FirEmptyPredicateBasedProvider)
register(FirRegisteredPluginAnnotations::class, FirRegisteredPluginAnnotationsImpl(this))
registerJavaComponents(JavaModuleResolver.getInstance(project))
val javaSymbolProvider = LLFirJavaSymbolProvider(this, moduleData, project, firProvider.searchScope)
register(JavaSymbolProvider::class, javaSymbolProvider)
//TODO: remove, replace with plugin loading
run {
register(FirPredicateBasedProvider::class, FirEmptyPredicateBasedProvider)
register(FirRegisteredPluginAnnotations::class, FirRegisteredPluginAnnotationsImpl(this))
}
val syntheticFunctionInterfaceProvider = FirExtensionSyntheticFunctionInterfaceProvider
.createIfNeeded(this, moduleData, scopeProvider)
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
this,
providers = listOfNotNull(
firProvider.symbolProvider,
syntheticFunctionInterfaceProvider,
javaSymbolProvider
),
dependencyProvider
)
val context = CodeFragmentSessionCreationContext(
moduleData,
firProvider,
dependencyProvider,
syntheticFunctionInterfaceProvider
)
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
additionalSessionConfiguration(context, this)
}
}
protected class CodeFragmentSessionCreationContext(
val moduleData: LLFirModuleData,
val firProvider: LLFirProvider,
val dependencyProvider: LLFirDependenciesSymbolProvider,
val syntheticFunctionInterfaceProvider: FirExtensionSyntheticFunctionInterfaceProvider?
)
private fun wrapLanguageVersionSettings(original: LanguageVersionSettings): LanguageVersionSettings {
return object : LanguageVersionSettings by original {
override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider
@@ -73,6 +74,25 @@ internal class LLFirCommonSessionFactory(project: Project) : LLFirAbstractSessio
}
}
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) {
registerDefaultComponents()
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
this,
providers = listOf(
firProvider.symbolProvider,
),
dependencyProvider,
)
)
registerPlatformSpecificComponentsIfAny(module)
}
}
override fun createProjectLibraryProvidersForScope(
session: LLFirSession,
moduleData: LLFirModuleData,
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.fir.BuiltinTypes
@@ -74,6 +75,25 @@ internal class LLFirJsSessionFactory(project: Project) : LLFirAbstractSessionFac
}
}
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) {
registerDefaultComponents()
registerModuleIndependentJsComponents()
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
this,
providers = listOfNotNull(
firProvider.symbolProvider,
syntheticFunctionInterfaceProvider,
),
dependencyProvider,
)
)
}
}
override fun createProjectLibraryProvidersForScope(
session: LLFirSession,
moduleData: LLFirModuleData,
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull
@OptIn(SessionConfiguration::class)
internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFactory(project) {
override fun createSourcesSession(module: KtSourceModule): LLFirSourcesSession {
return doCreateSourcesSession(module, FirKotlinScopeProvider(::wrapScopeWithJvmMapped)) { context ->
registerJavaComponents(JavaModuleResolver.getInstance(project))
@@ -79,6 +78,30 @@ internal class LLFirJvmSessionFactory(project: Project) : LLFirAbstractSessionFa
}
}
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) {
registerJavaComponents(JavaModuleResolver.getInstance(project))
val javaSymbolProvider = LLFirJavaSymbolProvider(this, moduleData, project, firProvider.searchScope)
register(JavaSymbolProvider::class, javaSymbolProvider)
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
this,
providers = listOfNotNull(
firProvider.symbolProvider,
syntheticFunctionInterfaceProvider,
javaSymbolProvider
),
dependencyProvider
)
)
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
}
}
override fun createProjectLibraryProvidersForScope(
session: LLFirSession,
moduleData: LLFirModuleData,
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirLi
import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.LLFirModuleData
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirModuleWithDependenciesSymbolProvider
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.KtCodeFragmentModule
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.analysis.providers.createPackagePartProvider
@@ -71,6 +72,24 @@ internal class LLFirNativeSessionFactory(project: Project) : LLFirAbstractSessio
}
}
override fun createCodeFragmentSession(module: KtCodeFragmentModule, contextSession: LLFirSession): LLFirSession {
return doCreateCodeFragmentSession(module, contextSession) {
registerModuleIndependentNativeComponents()
register(
FirSymbolProvider::class,
LLFirModuleWithDependenciesSymbolProvider(
this,
providers = listOfNotNull(
firProvider.symbolProvider,
syntheticFunctionInterfaceProvider,
),
dependencyProvider,
)
)
}
}
override fun createProjectLibraryProvidersForScope(
session: LLFirSession,
moduleData: LLFirModuleData,