diff --git a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/KtFe10ModuleRegistrarPreAnalysisHandler.kt b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/KtFe10ModuleRegistrarPreAnalysisHandler.kt index b931f5588e2..76c9a1b7acd 100644 --- a/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/KtFe10ModuleRegistrarPreAnalysisHandler.kt +++ b/analysis/analysis-api-fe10/tests/org/jetbrains/kotlin/analysis/api/descriptors/test/KtFe10ModuleRegistrarPreAnalysisHandler.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.api.descriptors.test import com.intellij.mock.MockProject import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtSourceModule import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory @@ -34,7 +35,7 @@ class KtFe10ModuleRegistrarPreAnalysisHandler( ktFilesByModule.forEach { (testModule, ktFiles) -> val project = testServices.compilerConfigurationProvider.getProject(testModule) - moduleInfoProvider.registerModuleInfo(project, testModule, ktFiles) + moduleInfoProvider.registerModuleInfo(testModule, TestKtSourceModule(project, testModule, ktFiles, testServices)) with(project as MockProject) { registerService(KotlinModificationTrackerFactory::class.java, KotlinStaticModificationTrackerFactory::class.java) diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirFrontendApiTestConfiguratorService.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirFrontendApiTestConfiguratorService.kt index 16284553f7c..3b415577165 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirFrontendApiTestConfiguratorService.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/FirFrontendApiTestConfiguratorService.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService +import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.registerTestServices import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.FirLowLevelFrontendApiTestConfiguratorService import org.jetbrains.kotlin.idea.references.KotlinFirReferenceContributor import org.jetbrains.kotlin.idea.references.KotlinReferenceProviderContributor @@ -32,10 +33,8 @@ object FirFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorServic return FirLowLevelFrontendApiTestConfiguratorService.getOriginalFile(file) } - @OptIn(InvalidWayOfUsingAnalysisSession::class) override fun registerProjectServices(project: MockProject) { FirLowLevelFrontendApiTestConfiguratorService.registerProjectServices(project) - project.registerService(KtAnalysisSessionProvider::class.java, KtFirAnalysisSessionProvider::class.java) } override fun registerApplicationServices(application: MockApplication) { diff --git a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/AbstractFrontendApiTest.kt b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/AbstractFrontendApiTest.kt index 832d331f6bb..d8e8955de7a 100644 --- a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/AbstractFrontendApiTest.kt +++ b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/AbstractFrontendApiTest.kt @@ -33,8 +33,8 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInfo import java.nio.file.Path import java.nio.file.Paths -import kotlin.io.path.exists import java.util.concurrent.ExecutionException +import kotlin.io.path.exists import kotlin.io.path.nameWithoutExtension interface FrontendApiTestConfiguratorService { @@ -56,7 +56,7 @@ interface FrontendApiTestConfiguratorService { fun doOutOfBlockModification(file: KtFile) - fun preprocessTestDataPath(path: Path) : Path = path + fun preprocessTestDataPath(path: Path): Path = path } abstract class AbstractFrontendApiTest(val configurator: FrontendApiTestConfiguratorService) : TestWithDisposable() { @@ -99,7 +99,7 @@ abstract class AbstractFrontendApiTest(val configurator: FrontendApiTestConfigur } } - private fun getTestDataFileSiblingPath(extension: String, testPrefix: String?): Path { + protected fun getTestDataFileSiblingPath(extension: String, testPrefix: String?): Path { val extensionWithDot = "." + extension.removePrefix(".") val baseName = testDataPath.nameWithoutExtension @@ -139,6 +139,13 @@ abstract class AbstractFrontendApiTest(val configurator: FrontendApiTestConfigur this.testInfo = this@AbstractFrontendApiTest.testInfo } + protected open fun handleInitializationError(exception: Throwable, moduleStructure: TestModuleStructure): InitializationErrorAction = + InitializationErrorAction.THROW + + enum class InitializationErrorAction { + IGNORE, THROW + } + protected fun runTest(@TestDataFile path: String) { testDataPath = configurator.preprocessTestDataPath(Paths.get(path)) val testConfiguration = testConfiguration(path, configure) @@ -147,26 +154,36 @@ abstract class AbstractFrontendApiTest(val configurator: FrontendApiTestConfigur val moduleStructure = testConfiguration.moduleStructureExtractor.splitTestDataByModules( path, testConfiguration.directives, - ).also { testModuleStructure -> - testConfiguration.testServices.register(TestModuleStructure::class, testModuleStructure) - testConfiguration.preAnalysisHandlers.forEach { preprocessor -> - preprocessor.preprocessModuleStructure(testModuleStructure) - } - } - + ) val singleModule = moduleStructure.modules.single() val project = testServices.compilerConfigurationProvider.getProject(singleModule) val moduleInfoProvider = testServices.projectModuleProvider - - val moduleInfo = moduleInfoProvider.getModule(singleModule.name) - with(project as MockProject) { configurator.registerProjectServices(this) } registerApplicationServices() + testConfiguration.testServices.register(TestModuleStructure::class, moduleStructure) + testConfiguration.preAnalysisHandlers.forEach { preprocessor -> + try { + preprocessor.preprocessModuleStructure(moduleStructure) + } catch (exception: Throwable) { + when (handleInitializationError(exception, moduleStructure)) { + InitializationErrorAction.IGNORE -> {} + InitializationErrorAction.THROW -> throw exception + } + } + } + + + val moduleInfo = moduleInfoProvider.getModule(singleModule.name) + + val ktFiles = when (moduleInfo) { + is TestKtSourceModule -> moduleInfo.testFilesToKtFiles.filterKeys { testFile -> !testFile.isAdditional }.values.toList() + is TestKtLibraryModule -> moduleInfo.ktFiles.toList() + else -> error("Unexpected $moduleInfo") + } - val ktFiles = moduleInfo.testFilesToKtFiles.filterKeys { testFile -> !testFile.isAdditional }.values.toList() configurator.prepareTestFiles(ktFiles, singleModule, testServices) doTestByFileStructure(ktFiles, moduleStructure, testServices) if (!enableTestInDependedMode || ktFiles.any { diff --git a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt index 11cd2bb3bde..c2f7b1c872f 100644 --- a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt +++ b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtModuleProvider.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlin.analysis.api.impl.barebone.test -import com.intellij.openapi.project.Project import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.services.TestService import org.jetbrains.kotlin.test.services.TestServices @@ -15,18 +13,18 @@ import org.jetbrains.kotlin.test.services.TestServices class TestKtModuleProvider( private val testServices: TestServices ) : TestService { - private val cache = mutableMapOf() + private val cache = mutableMapOf() - fun registerModuleInfo(project: Project, testModule: TestModule, ktFiles: Map) { - cache[testModule.name] = TestKtSourceModule(project, testModule, ktFiles, testServices) + fun registerModuleInfo(testModule: TestModule, ktModule: TestKtModule) { + cache[testModule.name] = ktModule } - fun getModuleInfoByKtFile(ktFile: KtFile): TestKtSourceModule = + fun getModuleInfoByKtFile(ktFile: KtFile): TestKtModule = cache.values.first { moduleSourceInfo -> (if (ktFile.isPhysical) ktFile else ktFile.originalFile) in moduleSourceInfo.ktFiles } - fun getModule(moduleName: String): TestKtSourceModule = + fun getModule(moduleName: String): TestKtModule = cache.getValue(moduleName) } diff --git a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtSourceModule.kt b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtSourceModule.kt index ca311c06840..d3a1baeb741 100644 --- a/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtSourceModule.kt +++ b/analysis/analysis-api-impl-barebone/tests/org/jetbrains/kotlin/analysis/api/impl/barebone/test/TestKtSourceModule.kt @@ -31,24 +31,23 @@ import java.nio.file.Path import java.nio.file.Paths @OptIn(ExperimentalStdlibApi::class) -class TestKtSourceModule( - override val project: Project, +abstract class TestKtModule( + val project: Project, val testModule: TestModule, - val testFilesToKtFiles: Map, + val ktFiles: Set, testServices: TestServices, -) : KtSourceModule { +) { private val moduleProvider = testServices.projectModuleProvider private val compilerConfigurationProvider = testServices.compilerConfigurationProvider private val configuration = compilerConfigurationProvider.getCompilerConfiguration(testModule) - val ktFiles = testFilesToKtFiles.values.toSet() - override val moduleName: String + val moduleName: String get() = testModule.name - override val directRegularDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + val directRegularDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { buildList { - testModule.allDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) } + testModule.allDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) as KtModule } addIfNotNull( libraryByRoots( (configuration.jvmModularRoots + configuration.jvmClasspathRoots).map(File::toPath) @@ -57,45 +56,72 @@ class TestKtSourceModule( } } - override val directRefinementDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + val directRefinementDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { testModule.dependsOnDependencies - .map { moduleProvider.getModule(it.moduleName) } + .map { moduleProvider.getModule(it.moduleName) as KtModule } } - override val directFriendDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + val directFriendDependencies: List by lazy(LazyThreadSafetyMode.PUBLICATION) { buildList { - testModule.friendDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) } + testModule.friendDependencies.mapTo(this) { moduleProvider.getModule(it.moduleName) as KtModule } addIfNotNull( libraryByRoots(configuration[JVMConfigurationKeys.FRIEND_PATHS].orEmpty().map(Paths::get)) ) } } + protected abstract val ktModule: KtModule + private fun libraryByRoots(roots: List): LibraryByRoots? { if (roots.isEmpty()) return null return LibraryByRoots( roots, - this@TestKtSourceModule, + ktModule, project, ) } - override val contentScope: GlobalSearchScope = - TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, testFilesToKtFiles.values) - - override val languageVersionSettings: LanguageVersionSettings + val languageVersionSettings: LanguageVersionSettings get() = testModule.languageVersionSettings - override val platform: TargetPlatform + val platform: TargetPlatform get() = testModule.targetPlatform - override val analyzerServices: PlatformDependentAnalyzerServices + val analyzerServices: PlatformDependentAnalyzerServices get() = testModule.targetPlatform.getAnalyzerServices() } +class TestKtSourceModule( + project: Project, + testModule: TestModule, + val testFilesToKtFiles: Map, + testServices: TestServices +) : TestKtModule(project, testModule, testFilesToKtFiles.values.toSet(), testServices), KtSourceModule { + override val ktModule: KtModule get() = this + + override val contentScope: GlobalSearchScope = + TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, testFilesToKtFiles.values) +} + +class TestKtLibraryModule( + project: Project, + testModule: TestModule, + ktFiles: Collection, + testServices: TestServices +) : TestKtModule(project, testModule, ktFiles.toSet(), testServices), KtLibraryModule { + override val ktModule: KtModule get() = this + override val libraryName: String get() = testModule.name + override val librarySources: KtLibrarySourceModule? get() = null + + override fun getBinaryRoots(): Collection = ktFiles.map { it.virtualFile.toNioPath() } + + override val contentScope: GlobalSearchScope = + GlobalSearchScope.filesScope(project, ktFiles.map { it.virtualFile }) +} + private class LibraryByRoots( private val roots: List, - private val sourceModule: KtSourceModule, + private val module: KtModule, override val project: Project, ) : KtLibraryModule { override val libraryName: String get() = "Test Library" @@ -103,8 +129,8 @@ private class LibraryByRoots( override val directRefinementDependencies: List get() = emptyList() override val directFriendDependencies: List get() = emptyList() override val contentScope: GlobalSearchScope get() = ProjectScope.getLibrariesScope(project) - override val platform: TargetPlatform get() = sourceModule.platform - override val analyzerServices: PlatformDependentAnalyzerServices get() = sourceModule.analyzerServices + override val platform: TargetPlatform get() = module.platform + override val analyzerServices: PlatformDependentAnalyzerServices get() = module.analyzerServices override fun getBinaryRoots(): Collection = roots override val librarySources: KtLibrarySourceModule? get() = null } diff --git a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/ClsJavaStubByVirtualFileCache.kt b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/ClsJavaStubByVirtualFileCache.kt index 86ba5cab940..c1187e0aa68 100644 --- a/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/ClsJavaStubByVirtualFileCache.kt +++ b/analysis/decompiled/light-classes-for-decompiled/src/org/jetbrains/kotlin/analysis/decompiled/light/classes/ClsJavaStubByVirtualFileCache.kt @@ -16,7 +16,7 @@ import com.intellij.util.cls.ClsFormatException import com.intellij.util.containers.ContainerUtil import java.io.IOException -internal class ClsJavaStubByVirtualFileCache { +class ClsJavaStubByVirtualFileCache { private class CachedJavaStub(val modificationStamp: Long, val javaFileStub: PsiJavaFileStubImpl) private val cache = ContainerUtil.createConcurrentWeakKeySoftValueMap() diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/AbstractCompilerBasedTestForFir.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/AbstractCompilerBasedTestForFir.kt index 31fe601f909..d5722cbbcd2 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/AbstractCompilerBasedTestForFir.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/AbstractCompilerBasedTestForFir.kt @@ -7,11 +7,13 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based import org.jetbrains.kotlin.analysis.api.impl.barebone.test.AbstractCompilerBasedTest import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtModuleProvider +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtSourceModule import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.api.DiagnosticCheckerFilter import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.createResolveStateForNoCaching import org.jetbrains.kotlin.analysis.low.level.api.fir.transformers.FirLazyTransformerForIDE +import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.test.bind import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder @@ -57,7 +59,7 @@ abstract class AbstractCompilerBasedTestForFir : AbstractCompilerBasedTest() { override fun analyze(module: TestModule): FirOutputArtifact { val moduleInfoProvider = testServices.projectModuleProvider - val moduleInfo = moduleInfoProvider.getModule(module.name) + val moduleInfo = moduleInfoProvider.getModule(module.name) as TestKtSourceModule val project = testServices.compilerConfigurationProvider.getProject(module) val resolveState = createResolveStateForNoCaching(moduleInfo, project) diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/ModuleRegistrarPreAnalysisHandler.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/ModuleRegistrarPreAnalysisHandler.kt index 633b634a628..12ee29776cb 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/ModuleRegistrarPreAnalysisHandler.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/ModuleRegistrarPreAnalysisHandler.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based import com.intellij.mock.MockProject import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtSourceModule import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getResolveState @@ -24,6 +25,7 @@ class ModuleRegistrarPreAnalysisHandler( ) : PreAnalysisHandler(testServices) { private val moduleInfoProvider = testServices.projectModuleProvider + override fun preprocessModuleStructure(moduleStructure: TestModuleStructure) { // todo rework after all modules will have the same Project instance val ktFilesByModule = moduleStructure.modules.associateWith { testModule -> @@ -35,7 +37,7 @@ class ModuleRegistrarPreAnalysisHandler( ktFilesByModule.forEach { (testModule, ktFiles) -> val project = testServices.compilerConfigurationProvider.getProject(testModule) - moduleInfoProvider.registerModuleInfo(project, testModule, ktFiles) + moduleInfoProvider.registerModuleInfo(testModule, TestKtSourceModule(project, testModule, ktFiles, testServices)) (project as MockProject).registerTestServices(testModule, allKtFiles, testServices) // Manually process all inheritors of sealed classes so that SealedClassInheritorsProviderTestImpl can work correctly for tests. @@ -57,4 +59,4 @@ class ModuleRegistrarPreAnalysisHandler( } } } -} \ No newline at end of file +} diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/testServices.kt b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/testServices.kt index c3ed17b2342..262b46e4f63 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/testServices.kt +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/based/testServices.kt @@ -85,7 +85,7 @@ private class KotlinProjectStructureProviderTestImpl(testServices: TestServices) private val moduleInfoProvider = testServices.projectModuleProvider override fun getKtModuleForKtElement(element: PsiElement): KtModule { val containingFile = element.containingFile as KtFile - return moduleInfoProvider.getModuleInfoByKtFile(containingFile) + return moduleInfoProvider.getModuleInfoByKtFile(containingFile) as KtModule } } diff --git a/analysis/symbol-light-classes/build.gradle.kts b/analysis/symbol-light-classes/build.gradle.kts index 9913acd3443..349cfb9c587 100644 --- a/analysis/symbol-light-classes/build.gradle.kts +++ b/analysis/symbol-light-classes/build.gradle.kts @@ -14,9 +14,21 @@ dependencies { implementation(project(":analysis:project-structure")) implementation(project(":analysis:decompiled:light-classes-for-decompiled")) implementation(intellijCore()) + + testImplementation(project(":analysis:decompiled:decompiler-to-file-stubs")) + testImplementation(projectTests(":analysis:analysis-api-impl-base")) + testImplementation(projectTests(":analysis:analysis-api-fir")) } sourceSets { "main" { projectDefault() } - "test" { none() } + "test" { projectDefault() } } + +projectTest(jUnitMode = JUnitMode.JUnit5) { + dependsOn(":dist") + workingDir = rootDir + useJUnitPlatform() +} + +testsJar() diff --git a/analysis/symbol-light-classes/testdata/additionalFiles/NotNull.java b/analysis/symbol-light-classes/testdata/additionalFiles/NotNull.java new file mode 100644 index 00000000000..62313f05bc1 --- /dev/null +++ b/analysis/symbol-light-classes/testdata/additionalFiles/NotNull.java @@ -0,0 +1,13 @@ +package org.jetbrains.annotations; + +import java.lang.annotation.*; + + +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE}) +public @interface NotNull { + String value() default ""; + + Class exception() default Exception.class; +} diff --git a/analysis/symbol-light-classes/testdata/additionalFiles/Nullable.java b/analysis/symbol-light-classes/testdata/additionalFiles/Nullable.java new file mode 100644 index 00000000000..98d19e886b3 --- /dev/null +++ b/analysis/symbol-light-classes/testdata/additionalFiles/Nullable.java @@ -0,0 +1,10 @@ +package org.jetbrains.annotations; + +import java.lang.annotation.*; + +@Documented +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE}) +public @interface Nullable { + @NonNls String value() default ""; +} diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesFacadeTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesFacadeTest.kt new file mode 100644 index 00000000000..f68e2376a0a --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesFacadeTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base + +import com.intellij.openapi.project.Project +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.renderClass +import org.jetbrains.kotlin.light.classes.symbol.base.service.withExtendedTypeRenderer +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.model.TestModule +import java.nio.file.Path + +abstract class AbstractSymbolLightClassesFacadeTest( + configurator: FrontendApiTestConfiguratorService, + override val currentExtension: String, + override val stopIfCompilationErrorDirectivePresent: Boolean, +) : AbstractSymbolLightClassesTestBase(configurator) { + override fun getRenderResult(ktFile: KtFile, testDataFile: Path, module: TestModule, project: Project): String { + val lightClasses = getFacades(project) + return withExtendedTypeRenderer((testDataFile)) { + lightClasses.joinToString("\n\n") { it.renderClass() } + } + } + + private fun getFacades( + project: Project + ): List { + val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(project) + val scope = GlobalSearchScope.allScope(project) + val facades = kotlinAsJavaSupport.getFacadeNames(FqName.ROOT, scope).sorted() + return facades + .flatMap { facadeName -> kotlinAsJavaSupport.getFacadeClasses(FqName(facadeName), scope) } + .filterIsInstance() + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesLoadingTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesLoadingTest.kt new file mode 100644 index 00000000000..cd818a0cd89 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesLoadingTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base + +import com.intellij.openapi.project.Project +import com.intellij.psi.SyntaxTraverser +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.renderClass +import org.jetbrains.kotlin.light.classes.symbol.base.service.withExtendedTypeRenderer +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.model.TestModule +import java.nio.file.Path + +abstract class AbstractSymbolLightClassesLoadingTest( + configurator: FrontendApiTestConfiguratorService, + override val currentExtension: String, + override val stopIfCompilationErrorDirectivePresent: Boolean, +) : AbstractSymbolLightClassesTestBase(configurator) { + + override fun getRenderResult(ktFile: KtFile, testDataFile: Path, module: TestModule, project: Project): String { + val lightClasses = getLightClassesFromFile(ktFile, project) + return withExtendedTypeRenderer(testDataFile) { + lightClasses.joinToString("\n\n") { it.renderClass() } + } + } + + private fun getLightClassesFromFile( + ktFile: KtFile, + project: Project + ): List { + val ktClasses = SyntaxTraverser.psiTraverser(ktFile).filter(KtClassOrObject::class.java).toList() + val kotlinAsJavaSupport = KotlinAsJavaSupport.getInstance(project) + return ktClasses.mapNotNull(kotlinAsJavaSupport::getLightClass) + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTest.kt new file mode 100644 index 00000000000..08f94d0733a --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.asJava.LightClassTestCommon +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.model.TestModule +import java.nio.file.Path + +abstract class AbstractSymbolLightClassesTest( + configurator: FrontendApiTestConfiguratorService, + override val currentExtension: String, + override val stopIfCompilationErrorDirectivePresent: Boolean, +) : AbstractSymbolLightClassesTestBase(configurator) { + override fun getRenderResult(ktFile: KtFile, testDataFile: Path, module: TestModule, project: Project): String { + return LightClassTestCommon.getActualLightClassText( + testDataFile.toFile(), + { fqName -> findLightClass(fqName, project) }, + LightClassTestCommon::removeEmptyDefaultImpls + ) + } + + private fun findLightClass(fqname: String, project: Project): PsiClass? { + return JavaElementFinder + .getInstance(project) + .findClass(fqname, GlobalSearchScope.allScope(project)) + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTestBase.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTestBase.kt new file mode 100644 index 00000000000..93068b93985 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/AbstractSymbolLightClassesTestBase.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClass +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.light.classes.symbol.base.service.NullabilityAnnotationSourceProvider +import org.jetbrains.kotlin.light.classes.symbol.decompiled.service.CompilerExecutor +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability +import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.TestModuleStructure +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions +import org.jetbrains.kotlin.test.utils.FirIdenticalCheckerHelper +import java.io.File +import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.nameWithoutExtension + +abstract class AbstractSymbolLightClassesTestBase( + configurator: FrontendApiTestConfiguratorService +) : AbstractHLApiSingleModuleTest(configurator) { + + override val enableTestInDependedMode: Boolean get() = false + + override fun configureTest(builder: TestConfigurationBuilder) { + super.configureTest(builder) + with(builder) { + useDirectives(Directives, CompilerExecutor.Directives) + useAdditionalSourceProviders(::NullabilityAnnotationSourceProvider) + } + } + + override fun handleInitializationError(exception: Throwable, moduleStructure: TestModuleStructure): InitializationErrorAction { + return if (Directives.IGNORE_FIR in moduleStructure.allDirectives) InitializationErrorAction.IGNORE + else InitializationErrorAction.THROW + } + + override fun doTestByFileStructure(ktFiles: List, module: TestModule, testServices: TestServices) { + if (stopIfCompilationErrorDirectivePresent && CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives) { + return + } + val testDataFile = module.files.first { it.name.endsWith(".kt") }.originalFile.toPath() + val project = ktFiles.first().project + + ignoreExceptionIfIgnoreFirPresent(module) { + val actual = getRenderResult(ktFiles.first(), testDataFile, module, project) + compareResults(testServices, actual) + removeIgnoreFir(module) + removeDuplicatedFirJava(testServices) + } + } + + protected abstract fun getRenderResult( + ktFile: KtFile, + testDataFile: Path, + module: TestModule, + project: Project + ): String + + private inline fun ignoreExceptionIfIgnoreFirPresent(module: TestModule, action: () -> Unit) { + try { + action() + } catch (e: Throwable) { + if (Directives.IGNORE_FIR in module.directives) { + return + } + throw e + } + } + + private fun compareResults( + testServices: TestServices, + actual: String, + ) { + if (currentResultPath().exists()) { + testServices.assertions.assertEqualsToFile(currentResultPath(), actual) + } else { + testServices.assertions.assertEqualsToFile(javaPath(), actual) + } + } + + private fun removeIgnoreFir(module: TestModule) { + if (Directives.IGNORE_FIR in module.directives) { + error("Test is passing. Please, remove `// ${Directives.IGNORE_FIR.name}` directive") + } + } + + private fun findLightClass(fqname: String, project: Project): PsiClass? { + return JavaElementFinder + .getInstance(project) + .findClass(fqname, GlobalSearchScope.allScope(project)) + } + + private fun removeDuplicatedFirJava(testServices: TestServices) { + val java = javaPath() + val firJava = currentResultPath() + if (!firJava.exists()) return + val identicalCheckerHelper = IdenticalCheckerHelper(testServices) + if (identicalCheckerHelper.contentsAreEquals(java.toFile(), firJava.toFile(), trimLines = true)) { + identicalCheckerHelper.deleteFirFile(java.toFile()) + } + } + + private inner class IdenticalCheckerHelper(testServices: TestServices) : FirIdenticalCheckerHelper(testServices) { + override fun getClassicFileToCompare(testDataFile: File): File { + return if (testDataFile.name.endsWith(EXTENSIONS.FIR_JAVA)) + testDataFile.resolveSibling(testDataFile.name.removeSuffix(currentExtension) + EXTENSIONS.JAVA) + else testDataFile + } + + override fun getFirFileToCompare(testDataFile: File): File { + return if (testDataFile.name.endsWith(EXTENSIONS.FIR_JAVA)) + testDataFile + else testDataFile.resolveSibling(testDataFile.name.removeSuffix(EXTENSIONS.JAVA) + currentExtension) + } + } + + private fun javaPath() = testDataPath.resolveSibling(testDataPath.nameWithoutExtension + EXTENSIONS.JAVA) + private fun currentResultPath() = testDataPath.resolveSibling(testDataPath.nameWithoutExtension + currentExtension) + + protected abstract val currentExtension: String + protected abstract val stopIfCompilationErrorDirectivePresent: Boolean + + object EXTENSIONS { + const val JAVA = ".java" + const val FIR_JAVA = ".fir.java" + const val LIB_JAVA = ".lib.java" + } + + private object Directives : SimpleDirectivesContainer() { + val IGNORE_FIR by directive( + description = "Ignore the test for Symbol FIR-based implementation of LC", + applicability = DirectiveApplicability.Global + ) + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/NullabilityAnnotationSourceProvider.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/NullabilityAnnotationSourceProvider.kt new file mode 100644 index 00000000000..0be53cb06ae --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/NullabilityAnnotationSourceProvider.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base.service + +import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives +import org.jetbrains.kotlin.test.model.TestFile +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.AdditionalSourceProvider +import org.jetbrains.kotlin.test.services.TestServices +import java.io.File + +class NullabilityAnnotationSourceProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) { + override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List { + return listOf( + "analysis/symbol-light-classes/testdata/additionalFiles/NotNull.java", + "analysis/symbol-light-classes/testdata/additionalFiles/Nullable.java", + ).map { path -> + File(path).toTestFile(relativePath = "org/jetbrains/annotations") + } + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/symbolLightClassTestUtils.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/symbolLightClassTestUtils.kt new file mode 100644 index 00000000000..a269422f84d --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/base/service/symbolLightClassTestUtils.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.base.service + +import org.jetbrains.kotlin.asJava.PsiClassRenderer +import java.nio.file.Path + +internal inline fun withExtendedTypeRenderer(testDataFile: Path, action: () -> R): R { + val extendedTypeRendererOld = PsiClassRenderer.extendedTypeRenderer + return try { + PsiClassRenderer.extendedTypeRenderer = testDataFile.toString().endsWith("typeAnnotations.kt") + action() + } finally { + PsiClassRenderer.extendedTypeRenderer = extendedTypeRendererOld + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesFacadeForLibraryTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesFacadeForLibraryTest.kt new file mode 100644 index 00000000000..618ba5e1de3 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesFacadeForLibraryTest.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled + +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesFacadeTest +import org.jetbrains.kotlin.light.classes.symbol.decompiled.service.SymbolLightClassesForLibraryFrontendApiTestConfiguratorService +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode + +abstract class AbstractSymbolLightClassesFacadeForLibraryTest : + AbstractSymbolLightClassesFacadeTest( + SymbolLightClassesForLibraryFrontendApiTestConfiguratorService, + EXTENSIONS.LIB_JAVA, + stopIfCompilationErrorDirectivePresent = true + ) \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesForLibraryTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesForLibraryTest.kt new file mode 100644 index 00000000000..3ba20997b30 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesForLibraryTest.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled + +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesTest +import org.jetbrains.kotlin.light.classes.symbol.decompiled.service.SymbolLightClassesForLibraryFrontendApiTestConfiguratorService +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode + +abstract class AbstractSymbolLightClassesForLibraryTest : + AbstractSymbolLightClassesTest( + SymbolLightClassesForLibraryFrontendApiTestConfiguratorService, + EXTENSIONS.LIB_JAVA, + stopIfCompilationErrorDirectivePresent = true + ) \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesLoadingForLibraryTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesLoadingForLibraryTest.kt new file mode 100644 index 00000000000..163f9b06969 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/AbstractSymbolLightClassesLoadingForLibraryTest.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled + +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesLoadingTest +import org.jetbrains.kotlin.light.classes.symbol.decompiled.service.SymbolLightClassesForLibraryFrontendApiTestConfiguratorService +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode + +abstract class AbstractSymbolLightClassesLoadingForLibraryTest : + AbstractSymbolLightClassesLoadingTest( + SymbolLightClassesForLibraryFrontendApiTestConfiguratorService, + EXTENSIONS.LIB_JAVA, + stopIfCompilationErrorDirectivePresent = true + ) diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/CompilerExecutor.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/CompilerExecutor.kt new file mode 100644 index 00000000000..3bae8570aa8 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/CompilerExecutor.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled.service + +import org.jetbrains.kotlin.test.MockLibraryUtil +import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives +import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives +import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer +import org.jetbrains.kotlin.test.model.TestModule +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.div +import kotlin.io.path.exists +import kotlin.io.path.notExists + +object CompilerExecutor { + fun compileLibrary(sourcesPath: Path, options: List, compilationErrorExpected: Boolean): Path? { + val library = sourcesPath / "library.jar" + val sourceFiles = sourcesPath.toFile().walkBottomUp() + val commands = buildList { + sourceFiles.mapTo(this) { it.absolutePath } + addAll(options) + add("-d") + add(library.absolutePathString()) + } + try { + MockLibraryUtil.runJvmCompiler(commands) + } catch (e: Throwable) { + if (!compilationErrorExpected) { + throw IllegalStateException("Unexpected compilation error while compiling library", e) + } + } + + if (library.exists() && compilationErrorExpected) { + error("Compilation error expected but, code was compiled successfully") + } + return library.takeIf(Path::exists) + } + + fun parseCompilerOptionsFromTestdata(module: TestModule): List = buildList { + module.directives[LanguageSettingsDirectives.API_VERSION].firstOrNull()?.let { apiVersion -> + addAll(listOf("-api-version", apiVersion.versionString)) + } + + module.directives[JvmEnvironmentConfigurationDirectives.JVM_TARGET].firstOrNull()?.let { jvmTarget -> + addAll(listOf("-jvm-target", jvmTarget.description)) + } + + addAll(module.directives[Directives.COMPILER_ARGUMENTS]) + } + + object Directives : SimpleDirectivesContainer() { + val COMPILER_ARGUMENTS by stringDirective("List of additional compiler arguments") + val COMPILATION_ERRORS by directive("Is compilation errors expected in the file") + } +} \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/LibraryModuleRegistrarPreAnalysisHandler.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/LibraryModuleRegistrarPreAnalysisHandler.kt new file mode 100644 index 00000000000..4640fc13c01 --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/LibraryModuleRegistrarPreAnalysisHandler.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled.service + +import com.intellij.mock.MockProject +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VfsUtilCore +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem +import com.intellij.psi.PsiManager +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtLibraryModule +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider +import org.jetbrains.kotlin.analysis.decompiler.psi.file.KtClsFile +import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.registerTestServices +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.* +import org.jetbrains.kotlin.test.util.KtTestUtil +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.createFile +import kotlin.io.path.div +import kotlin.io.path.writeText + + +class LibraryModuleRegistrarPreAnalysisHandler( + testServices: TestServices +) : PreAnalysisHandler(testServices) { + private val moduleInfoProvider = testServices.projectModuleProvider + + override fun preprocessModuleStructure(moduleStructure: TestModuleStructure) { + val testModule = moduleStructure.modules.single() + val project = testServices.compilerConfigurationProvider.getProject(testModule) + val decompiledKtFiles = getDecompiledVirtualFilesFromLibrary(testModule, project) + + moduleInfoProvider.registerModuleInfo(testModule, TestKtLibraryModule(project, testModule, decompiledKtFiles, testServices)) + (project as MockProject).registerTestServices(testModule, decompiledKtFiles, testServices) + } + + private fun getDecompiledVirtualFilesFromLibrary(module: TestModule, project: Project): List { + val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath() + for (testFile in module.files) { + val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile) + val tmpSourceFile = (tmpDir / testFile.name).createFile() + tmpSourceFile.writeText(text) + } + val library = CompilerExecutor.compileLibrary( + tmpDir, + CompilerExecutor.parseCompilerOptionsFromTestdata(module), + compilationErrorExpected = CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives + ) ?: return emptyList() + + val virtualFiles = getAllVirtualFilesFromLibrary(library) + return virtualFiles.mapNotNull { virtualFile -> + PsiManager.getInstance(project).findFile(virtualFile) as? KtClsFile + } + } + + + private fun getAllVirtualFilesFromLibrary(library: Path): Collection { + val jarFileSystem = CoreJarFileSystem() + val root = jarFileSystem.refreshAndFindFileByPath(library.absolutePathString() + "!/")!! + + val files = mutableSetOf() + VfsUtilCore.iterateChildrenRecursively( + root, + /*filter=*/{ true }, + /*iterator=*/{ virtualFile -> + if (!virtualFile.isDirectory) { + files.add(virtualFile) + } + true + } + ) + return files + } +} diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/SymbolLightClassesrFrontendApiTestConfiguratorService.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/SymbolLightClassesrFrontendApiTestConfiguratorService.kt new file mode 100644 index 00000000000..b8c6da53e8e --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/decompiled/service/SymbolLightClassesrFrontendApiTestConfiguratorService.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.decompiled.service + +import com.intellij.ide.highlighter.JavaClassFileType +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.Disposable +import com.intellij.openapi.extensions.LoadingOrder +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.ClassFileViewProviderFactory +import com.intellij.psi.FileTypeFileViewProviders +import com.intellij.psi.compiled.ClassFileDecompilers +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler +import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler +import org.jetbrains.kotlin.analysis.decompiler.stub.file.CachedAttributeData +import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache +import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import java.io.DataInput +import java.io.DataOutput + +object SymbolLightClassesForLibraryFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorService { + override fun TestConfigurationBuilder.configureTest(disposable: Disposable) { + usePreAnalysisHandlers(::LibraryModuleRegistrarPreAnalysisHandler) + } + + override fun registerProjectServices(project: MockProject) { + project.registerService(ClsJavaStubByVirtualFileCache::class.java) + } + + + override fun registerApplicationServices(application: MockApplication) { + FirFrontendApiTestConfiguratorService.registerApplicationServices(application) + if (application.getServiceIfCreated(ClsKotlinBinaryClassCache::class.java) != null) return + application.registerService(ClsKotlinBinaryClassCache::class.java) + application.registerService(FileAttributeService::class.java, DummyFileAttributeService) + FileTypeFileViewProviders.INSTANCE.addExplicitExtension( + JavaClassFileType.INSTANCE, + ClassFileViewProviderFactory() + ) + @Suppress("DEPRECATION") + ClassFileDecompilers.getInstance().EP_NAME.point.registerExtension(KotlinClassFileDecompiler(), LoadingOrder.FIRST) + @Suppress("DEPRECATION") + ClassFileDecompilers.getInstance().EP_NAME.point.registerExtension(KotlinBuiltInDecompiler(), LoadingOrder.FIRST) + } + + override fun doOutOfBlockModification(file: KtFile) { + FirFrontendApiTestConfiguratorService.doOutOfBlockModification(file) + } +} + +private object DummyFileAttributeService : FileAttributeService { + override fun write(file: VirtualFile, id: String, value: T, writeValueFun: (DataOutput, T) -> Unit): CachedAttributeData { + return CachedAttributeData(value, 0) + } + + override fun read(file: VirtualFile, id: String, readValueFun: (DataInput) -> T): CachedAttributeData? { + return null + } +} diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesFacadeForSourceTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesFacadeForSourceTest.kt new file mode 100644 index 00000000000..8f76c3101cf --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesFacadeForSourceTest.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.source + +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesFacadeTest + +abstract class AbstractSymbolLightClassesFacadeForSourceTest : + AbstractSymbolLightClassesFacadeTest( + FirFrontendApiTestConfiguratorService, + EXTENSIONS.FIR_JAVA, + stopIfCompilationErrorDirectivePresent = false + ) \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesForSourceTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesForSourceTest.kt new file mode 100644 index 00000000000..381235882dc --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesForSourceTest.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.source + +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesTest + +abstract class AbstractSymbolLightClassesForSourceTest : + AbstractSymbolLightClassesTest( + FirFrontendApiTestConfiguratorService, + EXTENSIONS.FIR_JAVA, + stopIfCompilationErrorDirectivePresent = false + ) \ No newline at end of file diff --git a/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesLoadingForSourceTest.kt b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesLoadingForSourceTest.kt new file mode 100644 index 00000000000..336a8aed47c --- /dev/null +++ b/analysis/symbol-light-classes/tests/org/jetbrains/kotlin/light/classes/symbol/source/AbstractSymbolLightClassesLoadingForSourceTest.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2021 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.light.classes.symbol.source + +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService +import org.jetbrains.kotlin.light.classes.symbol.base.AbstractSymbolLightClassesLoadingTest + +abstract class AbstractSymbolLightClassesLoadingForSourceTest : + AbstractSymbolLightClassesLoadingTest( + FirFrontendApiTestConfiguratorService, + EXTENSIONS.FIR_JAVA, + stopIfCompilationErrorDirectivePresent = false + ) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 940d3caf1c8..306e28ff462 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -817,7 +817,8 @@ tasks { ":analysis:analysis-api:test", ":analysis:analysis-api-fir:test", ":analysis:analysis-api-fe10:test", - ":analysis:low-level-api-fir:test" + ":analysis:low-level-api-fir:test", + ":analysis:symbol-light-classes:test" ) } diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/AdditionalSourceProvider.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/AdditionalSourceProvider.kt index d0ed719b140..e7d747370f3 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/AdditionalSourceProvider.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/AdditionalSourceProvider.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.test.model.ServicesAndDirectivesContainer import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestModule import java.io.File +import java.nio.file.Paths abstract class AdditionalSourceProvider(val testServices: TestServices) : ServicesAndDirectivesContainer { /** @@ -22,9 +23,9 @@ abstract class AdditionalSourceProvider(val testServices: TestServices) : Servic return globalDirectives.contains(directive) || module.directives.contains(directive) } - protected fun File.toTestFile(): TestFile { + protected fun File.toTestFile(relativePath: String? = null): TestFile { return TestFile( - this.name, + relativePath?.let(Paths::get)?.resolve(name)?.toString() ?: name, this.readText(), originalFile = this, startLineNumberInOriginalFile = 0, diff --git a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/test/MockLibraryUtil.kt b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/test/MockLibraryUtil.kt index 016458d1175..6d53d4b71d9 100644 --- a/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/test/MockLibraryUtil.kt +++ b/compiler/tests-compiler-utils/tests/org/jetbrains/kotlin/test/MockLibraryUtil.kt @@ -165,7 +165,7 @@ object MockLibraryUtil { return jarFile } - private fun runJvmCompiler(args: List) { + fun runJvmCompiler(args: List) { runCompiler(compiler2JVMClass, args) } diff --git a/generators/analysis-api-generator/build.gradle.kts b/generators/analysis-api-generator/build.gradle.kts index 67670701de3..78df103665a 100644 --- a/generators/analysis-api-generator/build.gradle.kts +++ b/generators/analysis-api-generator/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { testApi(projectTests(":analysis:analysis-api-fir")) testApi(projectTests(":analysis:analysis-api-fe10")) testApi(projectTests(":analysis:decompiled:decompiler-to-file-stubs")) + testApi(projectTests(":analysis:symbol-light-classes")) testApi(intellijCore()) testApiJUnit5() } diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/GenerateAnalysisApiTests.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/GenerateAnalysisApiTests.kt index c0afafbe2a1..4470539fd97 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/GenerateAnalysisApiTests.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/GenerateAnalysisApiTests.kt @@ -19,4 +19,5 @@ private fun TestGroupSuite.generateTests() { generateAnalysisApiTests() generateFirLowLevelApiTests() generateDecompiledTests() + generateSymbolLightClassesTests() } diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/lightClasses.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/lightClasses.kt new file mode 100644 index 00000000000..2b026126859 --- /dev/null +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/lightClasses.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2021 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.generators.tests.analysis.api + +import org.jetbrains.kotlin.generators.TestGroupSuite +import org.jetbrains.kotlin.generators.util.TestGeneratorUtil +import org.jetbrains.kotlin.light.classes.symbol.decompiled.AbstractSymbolLightClassesFacadeForLibraryTest +import org.jetbrains.kotlin.light.classes.symbol.decompiled.AbstractSymbolLightClassesForLibraryTest +import org.jetbrains.kotlin.light.classes.symbol.decompiled.AbstractSymbolLightClassesLoadingForLibraryTest +import org.jetbrains.kotlin.light.classes.symbol.source.AbstractSymbolLightClassesFacadeForSourceTest +import org.jetbrains.kotlin.light.classes.symbol.source.AbstractSymbolLightClassesForSourceTest +import org.jetbrains.kotlin.light.classes.symbol.source.AbstractSymbolLightClassesLoadingForSourceTest + +internal fun TestGroupSuite.generateSymbolLightClassesTests() { + testGroup( + "analysis/symbol-light-classes/tests", + "compiler/testData", + ) { + run { + testClass { + model( + "asJava/lightClasses", + pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME + ) + } + + testClass { + model( + "asJava/lightClasses", + excludeDirs = listOf("compilationErrors"), + pattern = TestGeneratorUtil.KT_WITHOUT_DOTS_IN_NAME + ) + } + } + + run { + testClass { + model("asJava/ultraLightClasses", pattern = TestGeneratorUtil.KT) + } + + testClass { + model("asJava/ultraLightClasses", pattern = TestGeneratorUtil.KT) + } + } + + run { + testClass { + model("asJava/ultraLightFacades", pattern = TestGeneratorUtil.KT) + } + + testClass { + model("asJava/ultraLightFacades", pattern = TestGeneratorUtil.KT) + } + } + } +} \ No newline at end of file diff --git a/generators/test-generator/tests/org/jetbrains/kotlin/generators/util/TestGeneratorUtil.kt b/generators/test-generator/tests/org/jetbrains/kotlin/generators/util/TestGeneratorUtil.kt index 3a44ef9fb06..eae5f0de7b6 100644 --- a/generators/test-generator/tests/org/jetbrains/kotlin/generators/util/TestGeneratorUtil.kt +++ b/generators/test-generator/tests/org/jetbrains/kotlin/generators/util/TestGeneratorUtil.kt @@ -11,6 +11,7 @@ import java.lang.StringBuilder object TestGeneratorUtil { @Language("RegExp") const val KT_OR_KTS = """^(.+)\.(kt|kts)$""" + @Language("RegExp") const val KT = """^(.+)\.(kt)$""" @Language("RegExp") const val KT_OR_KTS_WITHOUT_DOTS_IN_NAME = """^([^.]+)\.(kt|kts)$""" @Language("RegExp") const val KT_WITHOUT_DOTS_IN_NAME = """^([^.]+)\.kt$"""