[Analysis API] Add tests for foreign values

This commit is contained in:
Yan Zhulanow
2024-02-05 18:30:30 +09:00
committed by Space Team
parent 2dd16e1179
commit 1f39bc9a18
27 changed files with 237 additions and 48 deletions
@@ -214,6 +214,12 @@ public class FirIdeNormalAnalysisLibrarySourceModuleCompilerFacilityTestGenerate
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/foreignValue.kt");
}
@Test
@TestMetadata("initializer.kt")
public void testInitializer() {
@@ -214,6 +214,12 @@ public class FirIdeNormalAnalysisSourceModuleCompilerFacilityTestGenerated exten
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/foreignValue.kt");
}
@Test
@TestMetadata("initializer.kt")
public void testInitializer() {
@@ -45,6 +45,24 @@ public class FirIdeDependentAnalysisSourceModuleContainingDeclarationProviderByR
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("codeFragmentFromInside.kt")
public void testCodeFragmentFromInside() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/codeFragmentFromInside.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValue.kt");
}
@Test
@TestMetadata("foreignValueScript.kt")
public void testForeignValueScript() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValueScript.kt");
}
@Test
@TestMetadata("localFunction.kt")
public void testLocalFunction() {
@@ -45,6 +45,24 @@ public class FirIdeNormalAnalysisSourceModuleContainingDeclarationProviderByRefe
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("codeFragmentFromInside.kt")
public void testCodeFragmentFromInside() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/codeFragmentFromInside.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValue.kt");
}
@Test
@TestMetadata("foreignValueScript.kt")
public void testForeignValueScript() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValueScript.kt");
}
@Test
@TestMetadata("localFunction.kt")
public void testLocalFunction() {
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModul
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFileClassProvider
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.NO_RUNTIME
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
@@ -51,6 +53,7 @@ object AnalysisApiBaseTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
//but getClasses is used during java resolve, thus it's required to return some PsiClass for such cases
registerService(KtFileClassProvider::class.java, KtClsFileClassProvider(project))
registerService(ClsJavaStubByVirtualFileCache::class.java, ClsJavaStubByVirtualFileCache())
registerService(ScriptDefinitionProvider::class.java, CliScriptDefinitionProvider())
}
}
@@ -1,18 +0,0 @@
/*
* 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.impl.base.test.configurators
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
import org.jetbrains.kotlin.scripting.compiler.plugin.definitions.CliScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.test.services.TestServices
object AnalysisApiScriptTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
override fun registerProjectServices(project: MockProject, testServices: TestServices) {
project.registerService(ScriptDefinitionProvider::class.java, CliScriptDefinitionProvider())
}
}
@@ -45,6 +45,24 @@ public class FirStandaloneNormalAnalysisSourceModuleContainingDeclarationProvide
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("codeFragmentFromInside.kt")
public void testCodeFragmentFromInside() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/codeFragmentFromInside.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValue.kt");
}
@Test
@TestMetadata("foreignValueScript.kt")
public void testForeignValueScript() {
runTest("analysis/analysis-api/testData/components/containingDeclarationProvider/containingDeclarationByReference/foreignValueScript.kt");
}
@Test
@TestMetadata("localFunction.kt")
public void testLocalFunction() {
@@ -66,7 +66,7 @@ abstract class AbstractStandaloneSessionBuilderAgainstStdlibTest : TestWithDispo
// builtin type
val typeReference = ktFile.findDescendantOfType<KtNamedFunction>()!!.typeReference!!
typeReference.assertIsReferenceTo(StandardClassIds.Unit)
typeReference.assertIsReferenceTo(StandardClassIds.Float)
}
private fun KtTypeReference.assertIsReferenceTo(classId: ClassId) {
@@ -0,0 +1,3 @@
ForeignValue[name: foo_DebugLabel; isMutated: false; displayText: foo_DebugLabel]
lval foo_DebugLabel: R|kotlin/String!|
R|kotlin/String!|
@@ -0,0 +1,12 @@
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:@[FlexibleNullability] kotlin.String?) returnType:@[FlexibleNullability] kotlin.String?
VALUE_PARAMETER name:p0 index:0 type:@[FlexibleNullability] kotlin.String?
EXPRESSION_BODY
BLOCK type=@[FlexibleNullability] kotlin.String? origin=null
GET_VAR 'p0: @[FlexibleNullability] kotlin.String? declared in <root>.CodeFragment.run' type=@[FlexibleNullability] kotlin.String? origin=null
@@ -0,0 +1,18 @@
// MODULE: context
// FILE: context.kt
fun test() {
<caret_context>call("foo")
}
fun call(text: String) {}
// MODULE: main
// MODULE_KIND: CodeFragment
// CONTEXT_MODULE: context
// FILE: fragment.kt
// CODE_FRAGMENT_KIND: EXPRESSION
// CODE_FRAGMENT_FOREIGN_VALUE: foo_DebugLabel(Ljava/lang/String;)
<caret>foo_DebugLabel
@@ -0,0 +1,5 @@
public final class CodeFragment {
// source: 'fragment.kt'
public method <init>(): void
public final static method run(p0: java.lang.String): java.lang.String
}
@@ -0,0 +1,21 @@
// MODULE: context
// FILE: context.kt
fun test() {
<caret_context>call("foo")
}
fun call(text: String) {}
// MODULE: main
// MODULE_KIND: CodeFragment
// CONTEXT_MODULE: context
// FILE: fragment.kt
// CODE_FRAGMENT_KIND: BLOCK
fun local() {
call("bar")
}
<caret>local()
@@ -0,0 +1,18 @@
// MODULE: context
// FILE: context.kt
fun test() {
<caret_context>call("foo")
}
fun call(text: String) {}
// MODULE: main
// MODULE_KIND: CodeFragment
// CONTEXT_MODULE: context
// FILE: fragment.kt
// CODE_FRAGMENT_KIND: EXPRESSION
// CODE_FRAGMENT_FOREIGN_VALUE: foo_DebugLabel(Ljava/lang/String;)
<caret>foo_DebugLabel
@@ -0,0 +1,20 @@
// MODULE: lib
// FILE: lib.kt
fun call(text: String) {}
// MODULE: context(lib)
// FILE: context.kts
<caret_context>call("foo")
// MODULE: main
// MODULE_KIND: CodeFragment
// CONTEXT_MODULE: context
// FILE: fragment.kt
// CODE_FRAGMENT_KIND: EXPRESSION
// CODE_FRAGMENT_FOREIGN_VALUE: foo_DebugLabel(Ljava/lang/String;)
<caret>foo_DebugLabel
@@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
abstract class TestWithDisposable {
private var _disposable: Disposable? = null
private var _disposable: Disposable? = Disposer.newDisposable("Disposable for Analysis Api tests")
protected val disposable: Disposable get() = _disposable!!
@BeforeEach
@@ -43,7 +43,7 @@ class AnalysisApiKtModuleProviderImpl(
require(!this::modulesByName.isInitialized)
this.modulesStructure = modules
this.modulesByName = modulesStructure.mainModules.associateByName()
this.modulesByName = modulesStructure.mainModules.associateBy { it.testModule.name }
}
override fun getModuleStructure(): KtTestModuleProjectStructure = modulesStructure
@@ -57,21 +57,4 @@ fun TestServices.allKtFiles(): List<KtFile> = moduleStructure.modules.flatMap(kt
val TestServices.ktModuleProvider: AnalysisApiKtModuleProvider by TestServices.testServiceAccessor()
fun TestModule.getKtModule(testServices: TestServices): KtModule = testServices.ktModuleProvider.getModule(name)
fun List<KtTestModule>.associateByName(): Map<String, KtTestModule> {
return associateBy { ktTestModule ->
when (val ktModule = ktTestModule.ktModule) {
is KtModuleByCompilerConfiguration -> ktModule.moduleName
is KtSourceModule -> ktModule.moduleName
is KtLibraryModule -> ktModule.libraryName
is KtLibrarySourceModule -> ktModule.libraryName
is KtSdkModule -> ktModule.sdkName
is KtBuiltinsModule -> "Builtins for ${ktModule.platform}"
is KtNotUnderContentRootModule -> ktModule.name
is KtScriptModule -> ktModule.file.name
is KtDanglingFileModule -> ktModule.file.name
else -> error("Unsupported module type: " + ktModule.javaClass.name)
}
}
}
fun TestModule.getKtModule(testServices: TestServices): KtModule = testServices.ktModuleProvider.getModule(name)
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.analysis.test.framework.project.structure
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.project.structure.DanglingFileResolutionMode
import org.jetbrains.kotlin.analysis.project.structure.impl.KtDanglingFileModuleImpl
import org.jetbrains.kotlin.analysis.test.framework.services.TestForeignValue
import org.jetbrains.kotlin.analysis.test.framework.services.TestForeignValueProviderService
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind
import org.jetbrains.kotlin.psi.KtBlockCodeFragment
@@ -17,12 +19,14 @@ import org.jetbrains.kotlin.psi.KtExpressionCodeFragment
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeCodeFragment
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.org.objectweb.asm.Type
import java.nio.file.Path
/**
@@ -66,6 +70,9 @@ object KtCodeFragmentTestModuleFactory : KtTestModuleFactory {
CodeFragmentKind.TYPE -> KtTypeCodeFragment(project, fileName, fileText, contextElement)
}
val foreignValues = testFile.directives[AnalysisApiTestCodeFragmentDirectives.CODE_FRAGMENT_FOREIGN_VALUE]
TestForeignValueProviderService.submitForeignValues(codeFragment, foreignValues)
val module = KtDanglingFileModuleImpl(
codeFragment,
contextModule.ktModule,
@@ -91,6 +98,19 @@ object AnalysisApiTestCodeFragmentDirectives : SimpleDirectivesContainer() {
description = "Import local to the code fragment content",
applicability = DirectiveApplicability.File
)
val CODE_FRAGMENT_FOREIGN_VALUE by valueDirective<TestForeignValue>(
description = "Value injected to a code fragment",
applicability = DirectiveApplicability.File,
parser = fun(rawText: String): TestForeignValue? {
val match = CODE_FRAGMENT_FOREIGN_VALUE_REGEX.matchEntire(rawText) ?: return null
val valueName = match.groupValues[1].also { require(it.isIdentifier()) }
val valueType = match.groupValues[2].also { Type.getType(it) } // Check that it is a valid type descriptor
return TestForeignValue(valueName, valueType)
}
)
private val CODE_FRAGMENT_FOREIGN_VALUE_REGEX = Regex("^(.+)\\((.+)\\)$")
}
enum class CodeFragmentKind {
@@ -49,7 +49,7 @@ object TestModuleStructureFactory {
): KtTestModuleProjectStructure {
val modules = createModules(moduleStructure, testServices, project)
val modulesByName = modules.associateByName()
val modulesByName = modules.associateBy { it.testModule.name }
val libraryCache: LibraryCache = mutableMapOf()
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2024 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.test.framework.services
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.analysis.providers.ForeignValueProviderService
import org.jetbrains.kotlin.psi.KtCodeFragment
class TestForeignValue(val name: String, val internalType: String)
class TestForeignValueProviderService : ForeignValueProviderService {
override fun getForeignValues(codeFragment: KtCodeFragment): Map<String, String> {
return codeFragment.getUserData(FOREIGN_VALUES_KEY) ?: emptyMap()
}
companion object {
private val FOREIGN_VALUES_KEY = Key<Map<String, String>>("TestForeignValues")
fun submitForeignValues(codeFragment: KtCodeFragment, values: List<TestForeignValue>) {
require(codeFragment.getUserData(FOREIGN_VALUES_KEY) == null)
val map = values.map { Pair(it.name, it.internalType) }.toMap()
codeFragment.putUserData(FOREIGN_VALUES_KEY, map)
}
}
}
@@ -77,6 +77,12 @@ public class CodeFragmentCapturingTestGenerated extends AbstractCodeFragmentCapt
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/extensionReceiverSmartCasted.kt");
}
@Test
@TestMetadata("foreignValue.kt")
public void testForeignValue() {
runTest("analysis/analysis-api/testData/components/compilerFacility/compilation/codeFragments/capturing/foreignValue.kt");
}
@Test
@TestMetadata("initializer.kt")
public void testInitializer() {
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.services.LLSealedInherito
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.NoOpKtCompilerPluginsProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.services.PackagePartProviderTestImpl
import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider
import org.jetbrains.kotlin.analysis.providers.ForeignValueProviderService
import org.jetbrains.kotlin.analysis.providers.PackagePartProviderFactory
import org.jetbrains.kotlin.analysis.test.framework.services.TestForeignValueProviderService
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.impl.testConfiguration
@@ -25,9 +27,9 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
}
override fun registerProjectServices(project: MockProject, testServices: TestServices) {
project.apply {
FirStandaloneServiceRegistrar.registerProjectServices(project)
FirStandaloneServiceRegistrar.registerProjectServices(project)
project.apply {
registerService(LLSealedInheritorsProviderFactory::class.java, LLSealedInheritorsProviderFactoryForTests())
registerService(PackagePartProviderFactory::class.java, PackagePartProviderTestImpl(testServices))
registerService(KtCompilerPluginsProvider::class.java, NoOpKtCompilerPluginsProvider)
@@ -41,5 +43,9 @@ object AnalysisApiFirTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {
FirStandaloneServiceRegistrar.registerApplicationServices(application)
application.apply {
registerService(ForeignValueProviderService::class.java, TestForeignValueProviderService())
}
}
}
@@ -9,7 +9,6 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiBaseTestServiceRegistrar
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiDecompiledCodeTestServiceRegistrar
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiScriptTestServiceRegistrar
import org.jetbrains.kotlin.analysis.project.structure.KtBinaryModule
import org.jetbrains.kotlin.analysis.project.structure.allDirectDependenciesOfType
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtScriptModuleByCompilerConfiguration
@@ -42,7 +41,6 @@ object FirLowLevelCompilerBasedTestConfigurator : AnalysisApiTestConfigurator()
AnalysisApiBaseTestServiceRegistrar,
AnalysisApiDecompiledCodeTestServiceRegistrar,
AnalysisApiFirTestServiceRegistrar,
AnalysisApiScriptTestServiceRegistrar,
)
override fun createModules(
@@ -6,10 +6,8 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.analysis.api.impl.base.test.configurators.AnalysisApiScriptTestServiceRegistrar
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtScriptTestModuleFactory
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtTestModuleFactory
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
open class AnalysisApiFirScriptTestConfigurator(analyseInDependentSession: Boolean) :
@@ -21,6 +19,4 @@ open class AnalysisApiFirScriptTestConfigurator(analyseInDependentSession: Boole
useAdditionalService<KtTestModuleFactory> { KtScriptTestModuleFactory }
}
}
override val serviceRegistrars: List<AnalysisApiTestServiceRegistrar> = super.serviceRegistrars + AnalysisApiScriptTestServiceRegistrar
}