From 26e923e3aec16a4989c42c301cfc8acedd27e92a Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Wed, 2 Feb 2022 00:02:36 -0800 Subject: [PATCH] AA: introduce static project structure provider by compiler configuration Also add standalone mode utils to configure Application/Project environments --- analysis/analysis-api-fir/build.gradle.kts | 1 - .../FirFrontendApiTestConfiguratorService.kt | 5 +- ...ibraryModuleRegistrarPreAnalysisHandler.kt | 4 +- ...SourceModuleRegistrarPreAnalysisHandler.kt | 9 +- .../api/impl/base/util/LibraryUtils.kt} | 10 +- .../base/test/AbstractReferenceResolveTest.kt | 58 +------ .../TestReferenceResolveResultRenderer.kt | 63 +++++++ .../api/impl/base/test/commonTestUtils.kt | 19 ++- .../analysis-api-standalone/build.gradle.kts | 45 +++++ .../standalone/RegisterComponentService.java | 24 +++ .../api/standalone/StandaloneModeUtils.kt | 154 ++++++++++++++++++ .../impl/KtModuleByCompilerConfiguration.kt | 90 ++++++++++ ...tructureProviderByCompilerConfiguration.kt | 52 ++++++ .../AbstractStandaloneModeSingleModuleTest.kt | 86 ++++++++++ .../StandaloneModeConfiguratorService.kt | 36 ++++ ...andaloneModeSingleModuleTestGenerated.java | 47 ++++++ .../standalone/singleModule/multipleFiles.kt | 25 +++ .../standalone/singleModule/multipleFiles.txt | 2 + .../standalone/singleModule/singleFile.kt | 8 + .../standalone/singleModule/singleFile.txt | 2 + analysis/build.gradle.kts | 1 + .../analysis/project/structure/KtModule.kt | 2 +- build.gradle.kts | 1 + .../analysis-api-generator/build.gradle.kts | 5 +- .../analysis/api/GenerateAnalysisApiTests.kt | 1 + .../tests/analysis/api/analysisApi.kt | 1 - .../generators/tests/analysis/api/dsl/dsl.kt | 17 +- .../tests/analysis/api/standalone.kt | 26 +++ .../build.gradle.kts | 5 + settings.gradle | 2 + 30 files changed, 713 insertions(+), 88 deletions(-) rename analysis/analysis-api-impl-base/{tests/org/jetbrains/kotlin/analysis/api/impl/base/test/utils/libraries/TestLibraryUtils.kt => src/org/jetbrains/kotlin/analysis/api/impl/base/util/LibraryUtils.kt} (78%) create mode 100644 analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/TestReferenceResolveResultRenderer.kt create mode 100644 analysis/analysis-api-standalone/build.gradle.kts create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/RegisterComponentService.java create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt create mode 100644 analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt create mode 100644 analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/AbstractStandaloneModeSingleModuleTest.kt create mode 100644 analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeConfiguratorService.kt create mode 100644 analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeSingleModuleTestGenerated.java create mode 100644 analysis/analysis-api/testData/standalone/singleModule/multipleFiles.kt create mode 100644 analysis/analysis-api/testData/standalone/singleModule/multipleFiles.txt create mode 100644 analysis/analysis-api/testData/standalone/singleModule/singleFile.kt create mode 100644 analysis/analysis-api/testData/standalone/singleModule/singleFile.txt create mode 100644 generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/standalone.kt create mode 100644 prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts diff --git a/analysis/analysis-api-fir/build.gradle.kts b/analysis/analysis-api-fir/build.gradle.kts index 4fe18164753..26944dc481b 100644 --- a/analysis/analysis-api-fir/build.gradle.kts +++ b/analysis/analysis-api-fir/build.gradle.kts @@ -1,4 +1,3 @@ - plugins { kotlin("jvm") id("jps-compatible") 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 3b415577165..ae5186a337a 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 @@ -8,11 +8,8 @@ package org.jetbrains.kotlin.analysis.api.fir import com.intellij.mock.MockApplication import com.intellij.mock.MockProject import com.intellij.openapi.Disposable -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 @@ -48,4 +45,4 @@ object FirFrontendApiTestConfiguratorService : FrontendApiTestConfiguratorServic override fun doOutOfBlockModification(file: KtFile) { FirLowLevelFrontendApiTestConfiguratorService.doOutOfBlockModification(file) } -} \ No newline at end of file +} diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/binary/LibraryModuleRegistrarPreAnalysisHandler.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/binary/LibraryModuleRegistrarPreAnalysisHandler.kt index f3e216633e3..4840364fdb1 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/binary/LibraryModuleRegistrarPreAnalysisHandler.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/binary/LibraryModuleRegistrarPreAnalysisHandler.kt @@ -10,8 +10,8 @@ import com.intellij.openapi.project.Project 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.api.impl.base.test.utils.libraries.TestLibraryUtils import org.jetbrains.kotlin.analysis.api.impl.base.test.utils.libraries.compiledLibraryProvider +import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils 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 @@ -38,7 +38,7 @@ class LibraryModuleRegistrarPreAnalysisHandler( private fun getDecompiledVirtualFilesFromLibrary(module: TestModule, project: Project): List { val library = testServices.compiledLibraryProvider.getCompiledLibrary(module).jar - val virtualFiles = TestLibraryUtils.getAllVirtualFilesFromJar(library) + val virtualFiles = LibraryUtils.getAllVirtualFilesFromJar(library) return virtualFiles.mapNotNull { virtualFile -> PsiManager.getInstance(project).findFile(virtualFile) as? KtClsFile } diff --git a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/source/LibrarySourceModuleRegistrarPreAnalysisHandler.kt b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/source/LibrarySourceModuleRegistrarPreAnalysisHandler.kt index fd8e7fba57c..07b2fced30f 100644 --- a/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/source/LibrarySourceModuleRegistrarPreAnalysisHandler.kt +++ b/analysis/analysis-api-fir/tests/org/jetbrains/kotlin/analysis/api/fir/utils/libraries/source/LibrarySourceModuleRegistrarPreAnalysisHandler.kt @@ -11,8 +11,8 @@ import com.intellij.psi.PsiManager import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.analysis.api.impl.barebone.test.TestKtLibrarySourceModule import org.jetbrains.kotlin.analysis.api.impl.barebone.test.projectModuleProvider -import org.jetbrains.kotlin.analysis.api.impl.base.test.utils.libraries.TestLibraryUtils import org.jetbrains.kotlin.analysis.api.impl.base.test.utils.libraries.compiledLibraryProvider +import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.based.registerTestServices import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule @@ -28,7 +28,6 @@ import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.compilerConfigurationProvider import java.nio.file.Path - class LibrarySourceModuleRegistrarPreAnalysisHandler( testServices: TestServices ) : PreAnalysisHandler(testServices) { @@ -41,7 +40,7 @@ class LibrarySourceModuleRegistrarPreAnalysisHandler( val libraryKtModule = KtTestLibraryModule(project, testModule, libraryJar) - val librarySourceKtFiles = TestLibraryUtils + val librarySourceKtFiles = LibraryUtils .getAllVirtualFilesFromJar(librarySourcesJar) .mapNotNull { PsiManager.getInstance(project).findFile(it) as? KtFile } @@ -69,7 +68,7 @@ private class KtTestLibraryModule( override val directFriendDependencies: List get() = emptyList() override val contentScope: GlobalSearchScope by lazy { - GlobalSearchScope.filesScope(project, TestLibraryUtils.getAllVirtualFilesFromJar(jar)) + GlobalSearchScope.filesScope(project, LibraryUtils.getAllVirtualFilesFromJar(jar)) } override fun getBinaryRoots(): Collection = listOf(jar) @@ -79,4 +78,4 @@ private class KtTestLibraryModule( override val platform: TargetPlatform get() = testModule.targetPlatform override val analyzerServices: PlatformDependentAnalyzerServices get() = testModule.targetPlatform.getAnalyzerServices() -} \ No newline at end of file +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/utils/libraries/TestLibraryUtils.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/util/LibraryUtils.kt similarity index 78% rename from analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/utils/libraries/TestLibraryUtils.kt rename to analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/util/LibraryUtils.kt index c2264e5cd0e..284b1de338a 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/utils/libraries/TestLibraryUtils.kt +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/util/LibraryUtils.kt @@ -3,18 +3,18 @@ * 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.utils.libraries +package org.jetbrains.kotlin.analysis.api.impl.base.util import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem +import com.intellij.util.io.URLUtil.JAR_SEPARATOR import java.nio.file.Path -import kotlin.io.path.absolutePathString -object TestLibraryUtils { +object LibraryUtils { fun getAllVirtualFilesFromJar(jar: Path): Collection { val jarFileSystem = CoreJarFileSystem() - val root = jarFileSystem.refreshAndFindFileByPath(jar.absolutePathString() + "!/")!! + val root = jarFileSystem.refreshAndFindFileByPath(jar.toAbsolutePath().toString() + JAR_SEPARATOR)!! val files = mutableSetOf() VfsUtilCore.iterateChildrenRecursively( @@ -29,4 +29,4 @@ object TestLibraryUtils { ) return files } -} \ No newline at end of file +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/AbstractReferenceResolveTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/AbstractReferenceResolveTest.kt index 14446ad785e..bde3f7a91a6 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/AbstractReferenceResolveTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/AbstractReferenceResolveTest.kt @@ -5,19 +5,15 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test -import com.intellij.psi.PsiReference -import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions import org.jetbrains.kotlin.analysis.api.components.RendererModifier import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider +import org.jetbrains.kotlin.analysis.api.impl.base.test.TestReferenceResolveResultRenderer.renderResolvedTo import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest import org.jetbrains.kotlin.analysis.api.symbols.* -import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol -import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind import org.jetbrains.kotlin.idea.references.KtReference -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder @@ -52,7 +48,7 @@ abstract class AbstractReferenceResolveTest : AbstractHLApiSingleModuleTest() { ) { val symbols = ktReferences.flatMap { it.resolveToSymbols() } checkReferenceResultForValidity(ktReferences, module, testServices, symbols) - renderResolvedTo(symbols) + renderResolvedTo(symbols, renderingOptions) } if (Directives.UNRESOLVED_REFERENCE in module.directives) { @@ -83,49 +79,6 @@ abstract class AbstractReferenceResolveTest : AbstractHLApiSingleModuleTest() { } } - private fun KtAnalysisSession.renderResolvedTo(symbols: List) = - symbols.map { renderResolveResult(it) } - .sorted() - .withIndex() - .joinToString(separator = "\n") { "${it.index}: ${it.value}" } - - - private fun KtAnalysisSession.renderResolveResult(symbol: KtSymbol): String { - return buildString { - symbolContainerFqName(symbol)?.let { fqName -> - append("(in $fqName) ") - } - when (symbol) { - is KtDeclarationSymbol -> append(symbol.render(renderingOptions)) - is KtPackageSymbol -> append("package ${symbol.fqName}") - is KtReceiverParameterSymbol -> { - append("extension receiver with type ") - append(symbol.type.render(renderingOptions.typeRendererOptions)) - } - else -> error("Unexpected symbol ${symbol::class}") - } - } - } - - @Suppress("unused")// KtAnalysisSession receiver - private fun KtAnalysisSession.symbolContainerFqName(symbol: KtSymbol): String? { - if (symbol is KtPackageSymbol || symbol is KtValueParameterSymbol) return null - val nonLocalFqName = when (symbol) { - is KtConstructorSymbol -> symbol.containingClassIdIfNonLocal?.asSingleFqName() - is KtCallableSymbol -> symbol.callableIdIfNonLocal?.asSingleFqName()?.parent() - is KtClassLikeSymbol -> symbol.classIdIfNonLocal?.asSingleFqName()?.parent() - else -> null - } - when (nonLocalFqName) { - null -> Unit - FqName.ROOT -> return "ROOT" - else -> return nonLocalFqName.asString() - } - val container = (symbol as? KtSymbolWithKind)?.getContainingSymbol() ?: return null - val parents = generateSequence(container) { it.getContainingSymbol() }.toList().asReversed() - return ": " + parents.joinToString(separator = ".") { (it as? KtNamedSymbol)?.name?.asString() ?: "" } - } - private object Directives : SimpleDirectivesContainer() { val UNRESOLVED_REFERENCE by directive( "Reference should be unresolved", @@ -137,9 +90,4 @@ abstract class AbstractReferenceResolveTest : AbstractHLApiSingleModuleTest() { sortNestedDeclarations = true ) - private fun PsiReference.unwrapMultiReferences(): List = when (this) { - is KtReference -> listOf(this) - is PsiMultiReference -> references.flatMap { it.unwrapMultiReferences() } - else -> error("Unexpected reference $this") - } -} \ No newline at end of file +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/TestReferenceResolveResultRenderer.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/TestReferenceResolveResultRenderer.kt new file mode 100644 index 00000000000..044a3f16aa3 --- /dev/null +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/TestReferenceResolveResultRenderer.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2022 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 + +import org.jetbrains.kotlin.analysis.api.KtAnalysisSession +import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions +import org.jetbrains.kotlin.analysis.api.symbols.* +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol +import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind +import org.jetbrains.kotlin.name.FqName + +object TestReferenceResolveResultRenderer { + fun KtAnalysisSession.renderResolvedTo( + symbols: List, + renderingOptions: KtDeclarationRendererOptions = KtDeclarationRendererOptions.DEFAULT + ) = + symbols.map { renderResolveResult(it, renderingOptions) } + .sorted() + .withIndex() + .joinToString(separator = "\n") { "${it.index}: ${it.value}" } + + private fun KtAnalysisSession.renderResolveResult( + symbol: KtSymbol, + renderingOptions: KtDeclarationRendererOptions + ): String { + return buildString { + symbolContainerFqName(symbol)?.let { fqName -> + append("(in $fqName) ") + } + when (symbol) { + is KtDeclarationSymbol -> append(symbol.render(renderingOptions)) + is KtPackageSymbol -> append("package ${symbol.fqName}") + is KtReceiverParameterSymbol -> { + append("extension receiver with type ") + append(symbol.type.render(renderingOptions.typeRendererOptions)) + } + else -> error("Unexpected symbol ${symbol::class}") + } + } + } + + @Suppress("unused")// KtAnalysisSession receiver + private fun KtAnalysisSession.symbolContainerFqName(symbol: KtSymbol): String? { + if (symbol is KtPackageSymbol || symbol is KtValueParameterSymbol) return null + val nonLocalFqName = when (symbol) { + is KtConstructorSymbol -> symbol.containingClassIdIfNonLocal?.asSingleFqName() + is KtCallableSymbol -> symbol.callableIdIfNonLocal?.asSingleFqName()?.parent() + is KtClassLikeSymbol -> symbol.classIdIfNonLocal?.asSingleFqName()?.parent() + else -> null + } + when (nonLocalFqName) { + null -> Unit + FqName.ROOT -> return "ROOT" + else -> return nonLocalFqName.asString() + } + val container = (symbol as? KtSymbolWithKind)?.getContainingSymbol() ?: return null + val parents = generateSequence(container) { it.getContainingSymbol() }.toList().asReversed() + return ": " + parents.joinToString(separator = ".") { (it as? KtNamedSymbol)?.name?.asString() ?: "" } + } +} diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/commonTestUtils.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/commonTestUtils.kt index ec61d8a6980..e84a50f09fb 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/commonTestUtils.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/commonTestUtils.kt @@ -8,14 +8,14 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.Computable import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.analyse -import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils.offsetToLineAndColumn -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.idea.references.KtReference +import org.jetbrains.kotlin.psi.* inline fun runReadAction(crossinline runnable: () -> T): T { return ApplicationManager.getApplication().runReadAction(Computable { runnable() }) @@ -51,3 +51,12 @@ fun String.indented(indent: Int): String { fun KtDeclaration.getNameWithPositionString(): String { return (presentation?.presentableText ?: name ?: this::class.simpleName) + "@" + position() } + +fun findReferencesAtCaret(mainKtFile: KtFile, caretPosition: Int): List = + mainKtFile.findReferenceAt(caretPosition)?.unwrapMultiReferences().orEmpty().filterIsInstance() + +fun PsiReference.unwrapMultiReferences(): List = when (this) { + is KtReference -> listOf(this) + is PsiMultiReference -> references.flatMap { it.unwrapMultiReferences() } + else -> error("Unexpected reference $this") +} diff --git a/analysis/analysis-api-standalone/build.gradle.kts b/analysis/analysis-api-standalone/build.gradle.kts new file mode 100644 index 00000000000..71e4f6dbbb5 --- /dev/null +++ b/analysis/analysis-api-standalone/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + kotlin("jvm") + id("jps-compatible") +} + +kotlin { + explicitApiWarning() +} + +dependencies { + implementation(intellijCore()) + implementation(project(":compiler:psi")) + implementation(project(":compiler:cli")) + api(project(":analysis:analysis-api")) + api(project(":analysis:analysis-api-providers")) + api(project(":analysis:project-structure")) + api(project(":analysis:analysis-api-fir")) + api(project(":analysis:low-level-api-fir")) + api(project(":analysis:symbol-light-classes")) + + testApi(projectTests(":analysis:analysis-api-impl-base")) + testApi(projectTests(":analysis:analysis-api-fir")) + + testApi(project(":kotlin-test:kotlin-test-junit")) + testApi(toolsJar()) + testApiJUnit5() +} + +kotlin { + explicitApi() +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +projectTest(jUnitMode = JUnitMode.JUnit5) { + dependsOn(":dist") + workingDir = rootDir + useJUnitPlatform() +} + +testsJar() + diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/RegisterComponentService.java b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/RegisterComponentService.java new file mode 100644 index 00000000000..1fe77833cc4 --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/RegisterComponentService.java @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2022 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.standalone; + +import com.intellij.mock.MockProject; +import org.jetbrains.annotations.TestOnly; +import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirResolveStateService; + +class RegisterComponentService { + static void registerLLFirResolveStateService(MockProject project) { + project.registerService( + LLFirResolveStateService.class, + new LLFirResolveStateService(project) + ); + } + + @TestOnly + static void unregisterLLFirResolveStateService(MockProject project) { + project.getPicoContainer().unregisterComponent(LLFirResolveStateService.class.getName()); + } +} diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt new file mode 100644 index 00000000000..b14e8d710f2 --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeUtils.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2010-2022 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.standalone + +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementFinder +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession +import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory +import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider +import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl +import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.project.structure.impl.ProjectStructureProviderByCompilerConfiguration +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory +import org.jetbrains.kotlin.analysis.providers.KotlinPackageProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticModificationTrackerFactory +import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPackageProviderFactory +import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport +import org.jetbrains.kotlin.asJava.finder.JavaElementFinder +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProvider +import org.jetbrains.kotlin.fir.declarations.SealedClassInheritorsProviderImpl +import org.jetbrains.kotlin.idea.references.KotlinFirReferenceContributor +import org.jetbrains.kotlin.idea.references.KotlinReferenceProviderContributor +import org.jetbrains.kotlin.light.classes.symbol.IDEKotlinAsJavaFirSupport +import org.jetbrains.kotlin.light.classes.symbol.caches.SymbolLightClassFacadeCache +import org.jetbrains.kotlin.load.kotlin.PackagePartProvider +import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService +import org.jetbrains.kotlin.psi.KtFile + +/** + * Configure Application environment for Analysis API standalone mode. + * + * In particular, this will register: + * * [KotlinReferenceProvidersService] + * * [KotlinReferenceProviderContributor] + */ +public fun configureApplicationEnvironment(app: MockApplication) { + if (app.getServiceIfCreated(KotlinReferenceProvidersService::class.java) == null) { + app.registerService( + KotlinReferenceProvidersService::class.java, + HLApiReferenceProviderService::class.java + ) + } + if (app.getServiceIfCreated(KotlinReferenceProviderContributor::class.java) == null) { + app.registerService( + KotlinReferenceProviderContributor::class.java, + KotlinFirReferenceContributor::class.java + ) + } +} + +/** + * Configure Project environment for Analysis API standalone mode. + * + * In particular, this will register: + * * [KtAnalysisSessionProvider] + * * [KotlinAsJavaSupport] (a FIR version) + * * [SymbolLightClassFacadeCache] for FIR light class support + * * [KotlinModificationTrackerFactory] + * * [LLFirResolveStateService] + * * [FirSealedClassInheritorsProcessorFactory] + * * [KtModuleScopeProvider] + * * [ProjectStructureProvider] + * * [KotlinDeclarationProviderFactory] + * * [KotlinPackageProviderFactory] + * * [PackagePartProviderFactory] + * + * Note that [ProjectStructureProvider] is built by using + * * given [ktFiles] as Kotlin sources + * * other Java sources in [compilerConfig] (set via [addJavaSourceRoots]) + * * JVM class paths in [compilerConfig] (set via [addJvmClasspathRoots]) as library. + */ +public fun configureProjectEnvironment( + project: MockProject, + compilerConfig: CompilerConfiguration, + ktFiles: List, + packagePartProvider: (GlobalSearchScope) -> PackagePartProvider, +) { + reRegisterJavaElementFinder(project) + + // FIR LC + project.registerService( + SymbolLightClassFacadeCache::class.java + ) + + project.picoContainer.registerComponentInstance( + KotlinModificationTrackerFactory::class.qualifiedName, + KotlinStaticModificationTrackerFactory() + ) + RegisterComponentService.registerLLFirResolveStateService(project) + project.picoContainer.registerComponentInstance( + FirSealedClassInheritorsProcessorFactory::class.qualifiedName, + object : FirSealedClassInheritorsProcessorFactory() { + override fun createSealedClassInheritorsProvider(): SealedClassInheritorsProvider { + return SealedClassInheritorsProviderImpl + } + } + ) + project.picoContainer.registerComponentInstance( + KtModuleScopeProvider::class.qualifiedName, + KtModuleScopeProviderImpl() + ) + + project.picoContainer.registerComponentInstance( + ProjectStructureProvider::class.qualifiedName, + ProjectStructureProviderByCompilerConfiguration(compilerConfig, project, ktFiles) + ) + project.picoContainer.registerComponentInstance( + KotlinDeclarationProviderFactory::class.qualifiedName, + KotlinStaticDeclarationProviderFactory(ktFiles) + ) + project.picoContainer.registerComponentInstance( + KotlinPackageProviderFactory::class.qualifiedName, + KotlinStaticPackageProviderFactory(ktFiles) + ) + project.picoContainer.registerComponentInstance( + PackagePartProviderFactory::class.qualifiedName, + object : PackagePartProviderFactory() { + override fun createPackagePartProviderForLibrary(scope: GlobalSearchScope): PackagePartProvider { + return packagePartProvider(scope) + } + } + ) +} + +@OptIn(InvalidWayOfUsingAnalysisSession::class) +private fun reRegisterJavaElementFinder(project: Project) { + PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java) + with(project as MockProject) { + picoContainer.registerComponentInstance( + KtAnalysisSessionProvider::class.qualifiedName, + KtFirAnalysisSessionProvider(this) + ) + picoContainer.unregisterComponent(KotlinAsJavaSupport::class.qualifiedName) + picoContainer.registerComponentInstance( + KotlinAsJavaSupport::class.qualifiedName, + IDEKotlinAsJavaFirSupport(project) + ) + } + @Suppress("DEPRECATION") + PsiElementFinder.EP.getPoint(project).registerExtension(JavaElementFinder(project)) +} diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt new file mode 100644 index 00000000000..72e93d23896 --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/KtModuleByCompilerConfiguration.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2022 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.project.structure.impl + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.kotlin.analysis.api.impl.base.util.LibraryUtils +import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule +import org.jetbrains.kotlin.analysis.project.structure.KtLibrarySourceModule +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule +import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM +import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.jvm.JdkPlatform +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices +import java.nio.file.Path + +internal abstract class BaseKtModuleByCompilerConfiguration( + private val compilerConfig: CompilerConfiguration, + val project: Project, +) { + val analyzerServices: PlatformDependentAnalyzerServices + get() = JvmPlatformAnalyzerServices + + val directFriendDependencies: List + get() = emptyList() + + val directRefinementDependencies: List + get() = emptyList() + + val languageVersionSettings: LanguageVersionSettings + get() = LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST) + + val moduleName: String + get() = compilerConfig.get(CommonConfigurationKeys.MODULE_NAME) ?: "" + + val platform: TargetPlatform + get() = TargetPlatform(setOf(JdkPlatform(JvmTarget.DEFAULT))) +} + +internal class KtSourceModuleByCompilerConfiguration( + compilerConfig: CompilerConfiguration, + project: Project, + ktFiles: List, +) : BaseKtModuleByCompilerConfiguration(compilerConfig, project), KtSourceModule { + override val directRegularDependencies: List by lazy { + (compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots).map { + KtLibraryModuleByCompilerConfiguration(compilerConfig, project, it.toPath()) + } + } + + override val contentScope: GlobalSearchScope = + TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles) +} + +internal class KtLibraryModuleByCompilerConfiguration( + compilerConfig: CompilerConfiguration, + project: Project, + private val jar: Path, +) : BaseKtModuleByCompilerConfiguration(compilerConfig, project), KtLibraryModule { + override val directRegularDependencies: List get() = emptyList() + + internal val virtualFiles: Collection by lazy { + LibraryUtils.getAllVirtualFilesFromJar(jar) + } + + override val libraryName: String + get() = moduleName + + override val librarySources: KtLibrarySourceModule? + get() = null + + override val contentScope: GlobalSearchScope = + GlobalSearchScope.filesScope(project, virtualFiles) + + private val binaryRoots by lazy { + listOf(jar) + } + + override fun getBinaryRoots(): Collection = binaryRoots +} diff --git a/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt new file mode 100644 index 00000000000..771278e9975 --- /dev/null +++ b/analysis/analysis-api-standalone/src/org/jetbrains/kotlin/analysis/project/structure/impl/ProjectStructureProviderByCompilerConfiguration.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2022 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.project.structure.impl + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.project.structure.KtModule +import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.cli.jvm.config.javaSourceRoots +import org.jetbrains.kotlin.psi.KtFile +import java.nio.file.Files +import java.nio.file.Paths + +internal class ProjectStructureProviderByCompilerConfiguration( + private val compilerConfig: CompilerConfiguration, + project: Project, + ktFiles: List, +) : ProjectStructureProvider() { + private val sourceFiles: Set by lazy { + buildSet { + compilerConfig.javaSourceRoots.forEach { srcRoot -> + val path = Paths.get(srcRoot) + if (Files.isDirectory(path)) { + // E.g., project/app/src + Files.walk(Paths.get(srcRoot)) + .filter(Files::isRegularFile) + .forEach { add(it.toString()) } + } else { + // E.g., project/app/src/some/pkg/main.kt + add(srcRoot) + } + } + } + } + + private val sourceModule = KtSourceModuleByCompilerConfiguration(compilerConfig, project, ktFiles) + + override fun getKtModuleForKtElement(element: PsiElement): KtModule { + val containingFilePath = element.containingFile.virtualFile.path + return if (containingFilePath in sourceFiles) { + sourceModule + } else { + sourceModule.directRegularDependencies.find { libModule -> + (libModule as KtLibraryModuleByCompilerConfiguration).virtualFiles.any { it.path == containingFilePath } + } ?: error("Can't find module for $containingFilePath") + } + } +} diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/AbstractStandaloneModeSingleModuleTest.kt b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/AbstractStandaloneModeSingleModuleTest.kt new file mode 100644 index 00000000000..17b74c39486 --- /dev/null +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/AbstractStandaloneModeSingleModuleTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2022 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.standalone + +import com.intellij.mock.MockProject +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession +import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider +import org.jetbrains.kotlin.analysis.api.components.KtDeclarationRendererOptions +import org.jetbrains.kotlin.analysis.api.components.RendererModifier +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.expressionMarkerProvider +import org.jetbrains.kotlin.analysis.api.impl.base.test.TestReferenceResolveResultRenderer.renderResolvedTo +import org.jetbrains.kotlin.analysis.api.impl.base.test.findReferencesAtCaret +import org.jetbrains.kotlin.analysis.api.impl.base.test.test.framework.AbstractHLApiSingleModuleTest +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory +import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory +import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider +import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider +import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory +import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory +import org.jetbrains.kotlin.analysis.providers.KotlinPackageProviderFactory +import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoots +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.assertions +import org.jetbrains.kotlin.test.services.compilerConfigurationProvider +import java.io.File + +abstract class AbstractStandaloneModeSingleModuleTest : AbstractHLApiSingleModuleTest() { + + override fun doTestByFileStructure(ktFiles: List, module: TestModule, testServices: TestServices) { + val project = testServices.compilerConfigurationProvider.getProject(module) as MockProject + // (project.picoContainer as DefaultPicoContainer).release() // is way too strong :( even PsiManager is gone + unregisterServices(project) + // TODO: better to be part of `StandaloneModeUtilsConfiguratorService` + val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module) + compilerConfiguration.addJavaSourceRoots(ktFiles.map { File(it.virtualFilePath) }) + configureProjectEnvironment( + project, + compilerConfiguration, + ktFiles, + testServices.compilerConfigurationProvider.getPackagePartProviderFactory(module) + ) + + val mainKtFile = ktFiles.singleOrNull() ?: ktFiles.first { it.name == "main.kt" } + val caretPosition = testServices.expressionMarkerProvider.getCaretPosition(mainKtFile) + val ktReferences = findReferencesAtCaret(mainKtFile, caretPosition) + if (ktReferences.isEmpty()) { + testServices.assertions.fail { "No references at caret found" } + } + + val resolvedTo = + analyseForTest( + PsiTreeUtil.findElementOfClassAtOffset(mainKtFile, caretPosition, KtDeclaration::class.java, false) ?: mainKtFile + ) { + val symbols = ktReferences.flatMap { it.resolveToSymbols() } + renderResolvedTo(symbols, renderingOptions) + } + + val actual = "Resolved to:\n$resolvedTo" + testServices.assertions.assertEqualsToTestDataFileSibling(actual) + } + + private val renderingOptions = KtDeclarationRendererOptions.DEFAULT.copy( + modifiers = RendererModifier.DEFAULT - RendererModifier.ANNOTATIONS, + sortNestedDeclarations = true + ) + + @OptIn(InvalidWayOfUsingAnalysisSession::class) + private fun unregisterServices(project: MockProject) { + project.picoContainer.unregisterComponent(KtAnalysisSessionProvider::class.qualifiedName) + project.picoContainer.unregisterComponent(KotlinModificationTrackerFactory::class.qualifiedName) + RegisterComponentService.unregisterLLFirResolveStateService(project) + project.picoContainer.unregisterComponent(FirSealedClassInheritorsProcessorFactory::class.qualifiedName) + project.picoContainer.unregisterComponent(KtModuleScopeProvider::class.qualifiedName) + project.picoContainer.unregisterComponent(ProjectStructureProvider::class.qualifiedName) + project.picoContainer.unregisterComponent(KotlinDeclarationProviderFactory::class.qualifiedName) + project.picoContainer.unregisterComponent(KotlinPackageProviderFactory::class.qualifiedName) + project.picoContainer.unregisterComponent(PackagePartProviderFactory::class.qualifiedName) + } +} diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeConfiguratorService.kt b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeConfiguratorService.kt new file mode 100644 index 00000000000..13f78cd16c4 --- /dev/null +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeConfiguratorService.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2022 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.standalone + +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder + +object StandaloneModeConfiguratorService : FrontendApiTestConfiguratorService { + override val allowDependedAnalysisSession: Boolean + get() = false + + override fun TestConfigurationBuilder.configureTest(disposable: Disposable) { + with(FirFrontendApiTestConfiguratorService) { configureTest(disposable) } + } + + // TODO: other variant to register project services by compiler configuration? + override fun registerProjectServices(project: MockProject) { + // Will be configured at the test + } + + override fun registerApplicationServices(application: MockApplication) { + configureApplicationEnvironment(application) + } + + override fun doOutOfBlockModification(file: KtFile) { + FirFrontendApiTestConfiguratorService.doOutOfBlockModification(file) + } +} diff --git a/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeSingleModuleTestGenerated.java b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeSingleModuleTestGenerated.java new file mode 100644 index 00000000000..a4301e633d6 --- /dev/null +++ b/analysis/analysis-api-standalone/tests/org/jetbrains/kotlin/analysis/api/standalone/StandaloneModeSingleModuleTestGenerated.java @@ -0,0 +1,47 @@ +/* + * 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.analysis.api.standalone; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService; +import org.jetbrains.kotlin.analysis.api.standalone.StandaloneModeConfiguratorService; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("analysis/analysis-api/testData/standalone/singleModule") +@TestDataPath("$PROJECT_ROOT") +public class StandaloneModeSingleModuleTestGenerated extends AbstractStandaloneModeSingleModuleTest { + @NotNull + @Override + public FrontendApiTestConfiguratorService getConfigurator() { + return StandaloneModeConfiguratorService.INSTANCE; + } + + @Test + public void testAllFilesPresentInSingleModule() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/standalone/singleModule"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("multipleFiles.kt") + public void testMultipleFiles() throws Exception { + runTest("analysis/analysis-api/testData/standalone/singleModule/multipleFiles.kt"); + } + + @Test + @TestMetadata("singleFile.kt") + public void testSingleFile() throws Exception { + runTest("analysis/analysis-api/testData/standalone/singleModule/singleFile.kt"); + } +} diff --git a/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.kt b/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.kt new file mode 100644 index 00000000000..4f346056a08 --- /dev/null +++ b/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.kt @@ -0,0 +1,25 @@ +// FILE: main.kt + +package test.pkg + +import test.pkg.ColorSpace.Companion.MaxId +import test.pkg.ColorSpace.Companion.MinId + +annotation class IntRange(val from: Long, val to: Long) + +private fun isSrgb( + @IntRange(from = MinId.toLong(), to = MaxId.toLong()) id: Int +): Boolean { + return if (id == 0) true else false +} + +// FILE: ColorSpace.kt + +package test.pkg + +abstract class ColorSpace { + internal companion object { + internal const val MinId = -1 + internal const val MaxId = 63 + } +} diff --git a/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.txt b/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.txt new file mode 100644 index 00000000000..18d920f4ac8 --- /dev/null +++ b/analysis/analysis-api/testData/standalone/singleModule/multipleFiles.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in test.pkg.ColorSpace.Companion) internal const val MinId: kotlin.Int diff --git a/analysis/analysis-api/testData/standalone/singleModule/singleFile.kt b/analysis/analysis-api/testData/standalone/singleModule/singleFile.kt new file mode 100644 index 00000000000..99da165e589 --- /dev/null +++ b/analysis/analysis-api/testData/standalone/singleModule/singleFile.kt @@ -0,0 +1,8 @@ +package test + +annotation class MyAnno + +@MyAnno +class Foo { + fun bar(): Int = 42 +} diff --git a/analysis/analysis-api/testData/standalone/singleModule/singleFile.txt b/analysis/analysis-api/testData/standalone/singleModule/singleFile.txt new file mode 100644 index 00000000000..0c4b9135fe5 --- /dev/null +++ b/analysis/analysis-api/testData/standalone/singleModule/singleFile.txt @@ -0,0 +1,2 @@ +Resolved to: +0: (in test.MyAnno) constructor() diff --git a/analysis/build.gradle.kts b/analysis/build.gradle.kts index aaa001be725..c9f5db567e0 100644 --- a/analysis/build.gradle.kts +++ b/analysis/build.gradle.kts @@ -9,6 +9,7 @@ tasks.register("analysisAllTests") { ":analysis:analysis-api:test", ":analysis:analysis-api-fir:test", ":analysis:analysis-api-fe10:test", + ":analysis:analysis-api-standalone:test", ":analysis:low-level-api-fir:test", ":analysis:symbol-light-classes:test" ) diff --git a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/KtModule.kt b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/KtModule.kt index 23cd4030a6a..5c8d54790ad 100644 --- a/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/KtModule.kt +++ b/analysis/project-structure/src/org/jetbrains/kotlin/analysis/project/structure/KtModule.kt @@ -110,7 +110,7 @@ public sealed interface KtBinaryModule : KtModule, KtModuleWithProject { } /** - * A module which represents a binary library. E.g, JAR o KLIB. + * A module which represents a binary library. E.g, JAR or KLIB. */ public interface KtLibraryModule : KtBinaryModule { public val libraryName: String diff --git a/build.gradle.kts b/build.gradle.kts index 193257e9184..116ebf3ec1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -303,6 +303,7 @@ extra["compilerArtifactsForIde"] = listOf( ":prepare:ide-plugin-dependencies:analysis-api-providers-for-ide", ":prepare:ide-plugin-dependencies:analysis-project-structure-for-ide", ":prepare:ide-plugin-dependencies:symbol-light-classes-for-ide", + ":prepare:ide-plugin-dependencies:analysis-api-standalone-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-ir-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-common-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-fe10-for-ide", diff --git a/generators/analysis-api-generator/build.gradle.kts b/generators/analysis-api-generator/build.gradle.kts index 78df103665a..388e32ced84 100644 --- a/generators/analysis-api-generator/build.gradle.kts +++ b/generators/analysis-api-generator/build.gradle.kts @@ -14,9 +14,10 @@ dependencies { testApi(projectTests(":generators:test-generator")) testApi(projectTests(":compiler:tests-common")) testApi(projectTests(":compiler:tests-spec")) - testApi(projectTests("::analysis:low-level-api-fir")) + testApi(projectTests(":analysis:low-level-api-fir")) testApi(projectTests(":analysis:analysis-api-fir")) testApi(projectTests(":analysis:analysis-api-fe10")) + testApi(projectTests(":analysis:analysis-api-standalone")) testApi(projectTests(":analysis:decompiled:decompiler-to-file-stubs")) testApi(projectTests(":analysis:symbol-light-classes")) testApi(intellijCore()) @@ -25,4 +26,4 @@ dependencies { val generateFrontendApiTests by generator("org.jetbrains.kotlin.generators.tests.analysis.api.GenerateAnalysisApiTestsKt") -testsJar() \ No newline at end of file +testsJar() 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 c0c0df4ef6e..c3bb6fa32a7 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 @@ -21,4 +21,5 @@ private fun TestGroupSuite.generateTests() { generateFirLowLevelApiTests() generateDecompiledTests() generateSymbolLightClassesTests() + generateStandaloneModeTests() } diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt index 48368d59395..06bca73d14f 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/analysisApi.kt @@ -42,7 +42,6 @@ import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.component import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.group import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.test import org.jetbrains.kotlin.generators.util.TestGeneratorUtil -import java.nio.file.Paths fun TestGroupSuite.generateAnalysisApiTests() { generateAnalysisApiComponentsTests() diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/dsl/dsl.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/dsl/dsl.kt index c4c8060688c..108cad1c353 100644 --- a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/dsl/dsl.kt +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/dsl/dsl.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.analysis.api.fir.FirFrontendApiTestConfiguratorServi import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.binary.LibraryFrontendApiTestConfiguratorService import org.jetbrains.kotlin.analysis.api.fir.utils.libraries.source.LibrarySourceFrontendApiTestConfiguratorService import org.jetbrains.kotlin.analysis.api.impl.barebone.test.FrontendApiTestConfiguratorService +import org.jetbrains.kotlin.analysis.api.standalone.StandaloneModeConfiguratorService import org.jetbrains.kotlin.generators.TestGroup import org.jetbrains.kotlin.generators.TestGroupSuite import org.jetbrains.kotlin.generators.getDefaultSuiteTestClassName @@ -27,6 +28,8 @@ internal sealed class TestModuleKind(val componentName: String) { object LIBRARY : TestModuleKind("library") object LIBRARY_SOURCE : TestModuleKind("librarySource") + object STANDALONE_MODE : TestModuleKind("StandaloneMode") + companion object { val SOURCE_ONLY by lazy(LazyThreadSafetyMode.NONE) { listOf(SOURCE) } val SOURCE_AND_LIBRARY_SOURCE by lazy(LazyThreadSafetyMode.NONE) { listOf(SOURCE, LIBRARY_SOURCE) } @@ -59,7 +62,6 @@ internal fun FirAndFe10TestGroup.test( } } - internal fun TestGroupSuite.test( baseClass: KClass<*>, addFe10: Boolean = true, @@ -69,20 +71,19 @@ internal fun TestGroupSuite.test( FirAndFe10TestGroup(this, directory = null, testModuleKinds).test(baseClass, addFe10, init) } - internal fun TestGroupSuite.group( directory: String, + testModuleKinds: List = TestModuleKind.SOURCE_ONLY, init: FirAndFe10TestGroup.() -> Unit, ) { - FirAndFe10TestGroup(this, directory, testModuleKinds = listOf(TestModuleKind.SOURCE)).init() + FirAndFe10TestGroup(this, directory, testModuleKinds).init() } - internal fun TestGroupSuite.component( directory: String, init: FirAndFe10TestGroup.() -> Unit, ) { - group("components/$directory", init) + group("components/$directory", init = init) } private fun FirAndFe10TestGroup.analysisApiTest( @@ -95,7 +96,7 @@ private fun FirAndFe10TestGroup.analysisApiTest( val fullTestPath = "analysis/analysis-api/testData" + directory?.let { "/$it" }.orEmpty() testGroup(testRoot, fullTestPath) { if (testModuleKinds.size == 1) { - analysisApiTestClass(frontend, moduleKind = TestModuleKind.SOURCE, testClass, prefixNeeded = false, init) + analysisApiTestClass(frontend, moduleKind = testModuleKinds.single(), testClass, prefixNeeded = false, init) } else { testModuleKinds.forEach { component -> analysisApiTestClass(frontend, component, testClass, prefixNeeded = true, init) @@ -138,7 +139,7 @@ private fun TestGroup.analysisApiTestClass( } } -private fun createConfigurator( +internal fun createConfigurator( frontend: Frontend, moduleKind: TestModuleKind ): KClass = when (frontend) { @@ -146,11 +147,13 @@ private fun createConfigurator( TestModuleKind.SOURCE -> FirFrontendApiTestConfiguratorService::class TestModuleKind.LIBRARY -> LibraryFrontendApiTestConfiguratorService::class TestModuleKind.LIBRARY_SOURCE -> LibrarySourceFrontendApiTestConfiguratorService::class + TestModuleKind.STANDALONE_MODE -> StandaloneModeConfiguratorService::class } Frontend.FE10 -> when (moduleKind) { TestModuleKind.SOURCE -> KtFe10FrontendApiTestConfiguratorService::class TestModuleKind.LIBRARY -> TODO("TestModuleKind.LIBRARY is unsupported for fe10") TestModuleKind.LIBRARY_SOURCE -> TODO("TestModuleKind.LIBRARY_SOURCE is unsupported for fe10") + TestModuleKind.STANDALONE_MODE -> TODO("TestModuleKind.STANDALONE_MODE is unsupported for fe10") } } diff --git a/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/standalone.kt b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/standalone.kt new file mode 100644 index 00000000000..8f108620304 --- /dev/null +++ b/generators/analysis-api-generator/tests/org/jetbrains/kotlin/generators/tests/analysis/api/standalone.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2022 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.analysis.api.standalone.AbstractStandaloneModeSingleModuleTest +import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.Frontend +import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.FrontendConfiguratorTestModel +import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.TestModuleKind +import org.jetbrains.kotlin.generators.tests.analysis.api.dsl.createConfigurator + +internal fun TestGroupSuite.generateStandaloneModeTests() { + testGroup( + "analysis/analysis-api-standalone/tests", + "analysis/analysis-api/testData" + ) { + val configurator = createConfigurator(Frontend.FIR, TestModuleKind.STANDALONE_MODE) + testClass { + method(FrontendConfiguratorTestModel(configurator)) + model("standalone/singleModule") + } + } +} diff --git a/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts b/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts new file mode 100644 index 00000000000..ccc04c9b0e9 --- /dev/null +++ b/prepare/ide-plugin-dependencies/analysis-api-standalone-for-ide/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + kotlin("jvm") +} + +publishJarsForIde(listOf(":analysis:analysis-api-standalone")) diff --git a/settings.gradle b/settings.gradle index e0f9475abf7..bfbd77be5ea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -374,6 +374,7 @@ if (!buildProperties.inJpsBuildIdeaSync) { ":prepare:ide-plugin-dependencies:analysis-api-providers-for-ide", ":prepare:ide-plugin-dependencies:analysis-project-structure-for-ide", ":prepare:ide-plugin-dependencies:symbol-light-classes-for-ide", + ":prepare:ide-plugin-dependencies:analysis-api-standalone-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-ir-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-common-for-ide", ":prepare:ide-plugin-dependencies:kotlin-compiler-fe10-for-ide", @@ -504,6 +505,7 @@ include ":generators:analysis-api-generator", ":analysis:kt-references", ":analysis:symbol-light-classes", ":analysis:project-structure", + ":analysis:analysis-api-standalone", ":analysis:analysis-api-fe10", ":analysis:decompiled:decompiler-to-psi", ":analysis:decompiled:decompiler-to-stubs",