From da0d1c0233fe4aaf3abae46fed8c886531b3969e Mon Sep 17 00:00:00 2001 From: Dmitrii Gridin Date: Fri, 1 Nov 2024 17:53:23 +0100 Subject: [PATCH] [Analysis API] forbid symbol creation for unrelated declarations Symbols can be created for psi elements only if they are in the context of the current KaSession. For instance, it is legal to create a symbol for a psi element from a dependency module, but it is illegal to create a symbol from a dependent module. ^KT-66783 Fixed --- .../components/KaFe10SymbolProvider.kt | 52 +++++------ .../api/fir/symbols/KaFirSymbolProvider.kt | 42 ++++----- .../analysis-api/analysis-api-impl-base.xml | 7 ++ .../base/components/KaBaseSymbolProvider.kt | 58 ++++++++---- .../test/cases/symbols/AbstractSymbolTest.kt | 25 +++++- .../analysis/api/symbols/KaSymbolProvider.kt | 9 ++ .../symbolByPsi/classFromDependentModule.kt | 2 +- .../classFromDependentModule.pretty.txt | 1 - .../symbolByPsi/classFromDependentModule.txt | 30 ------- .../functionFromDependentModule.kt | 2 +- .../functionFromDependentModule.pretty.txt | 1 - .../functionFromDependentModule.txt | 35 -------- .../propertyFromDependentModule.kt | 2 +- .../propertyFromDependentModule.pretty.txt | 1 - .../propertyFromDependentModule.txt | 90 ------------------- .../base/AbstractAnalysisApiBasedTest.kt | 21 +++++ 16 files changed, 150 insertions(+), 228 deletions(-) delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.pretty.txt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.txt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.pretty.txt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.txt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.pretty.txt delete mode 100644 analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.txt diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KaFe10SymbolProvider.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KaFe10SymbolProvider.kt index 6aa7f576c97c8..a02ed95c15fe0 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KaFe10SymbolProvider.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/components/KaFe10SymbolProvider.kt @@ -35,13 +35,13 @@ internal class KaFe10SymbolProvider( } override val KtFile.symbol: KaFileSymbol - get() = withValidityAssertion { KaFe10FileSymbol(this, this@KaFe10SymbolProvider.analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10FileSymbol(this, this@KaFe10SymbolProvider.analysisContext) } override val KtScript.symbol: KaScriptSymbol - get() = withValidityAssertion { KaFe10PsiScriptSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiScriptSymbol(this, analysisContext) } override val KtParameter.symbol: KaVariableSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { when { isFunctionTypeParameter -> error("Function type parameters are not supported in getParameterSymbol()") isLoopParameter -> KaFe10PsiLoopParameterLocalVariableSymbol(this, analysisContext) @@ -50,8 +50,8 @@ internal class KaFe10SymbolProvider( } override val KtNamedFunction.symbol: KaFunctionSymbol - get() = withValidityAssertion { - return if (hasBody() && (funKeyword == null || nameIdentifier == null)) { + get() = createPsiBasedSymbolWithValidityAssertion { + if (hasBody() && (funKeyword == null || nameIdentifier == null)) { KaFe10PsiAnonymousFunctionSymbol(this, analysisContext) } else { KaFe10PsiNamedFunctionSymbol(this, analysisContext) @@ -59,23 +59,23 @@ internal class KaFe10SymbolProvider( } override val KtConstructor<*>.symbol: KaConstructorSymbol - get() = withValidityAssertion { KaFe10PsiConstructorSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiConstructorSymbol(this, analysisContext) } override val KtTypeParameter.symbol: KaTypeParameterSymbol - get() = withValidityAssertion { KaFe10PsiTypeParameterSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiTypeParameterSymbol(this, analysisContext) } override val KtTypeAlias.symbol: KaTypeAliasSymbol - get() = withValidityAssertion { KaFe10PsiTypeAliasSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiTypeAliasSymbol(this, analysisContext) } override val KtEnumEntry.symbol: KaEnumEntrySymbol - get() = withValidityAssertion { KaFe10PsiEnumEntrySymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiEnumEntrySymbol(this, analysisContext) } override val KtFunctionLiteral.symbol: KaAnonymousFunctionSymbol - get() = withValidityAssertion { KaFe10PsiLiteralAnonymousFunctionSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiLiteralAnonymousFunctionSymbol(this, analysisContext) } override val KtProperty.symbol: KaVariableSymbol - get() = withValidityAssertion { - return if (isLocal) { + get() = createPsiBasedSymbolWithValidityAssertion { + if (isLocal) { KaFe10PsiLocalVariableSymbol(this, analysisContext) } else { KaFe10PsiKotlinPropertySymbol(this, analysisContext) @@ -83,10 +83,10 @@ internal class KaFe10SymbolProvider( } override val KtObjectLiteralExpression.symbol: KaAnonymousObjectSymbol - get() = withValidityAssertion { KaFe10PsiAnonymousObjectSymbol(objectDeclaration, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiAnonymousObjectSymbol(objectDeclaration, analysisContext) } override val KtObjectDeclaration.symbol: KaClassSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (isObjectLiteral()) KaFe10PsiAnonymousObjectSymbol(this, analysisContext) else @@ -94,7 +94,7 @@ internal class KaFe10SymbolProvider( } override val KtClassOrObject.classSymbol: KaClassSymbol? - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { when (this) { is KtEnumEntry -> null is KtObjectDeclaration -> symbol @@ -103,17 +103,17 @@ internal class KaFe10SymbolProvider( } override val KtClassOrObject.namedClassSymbol: KaNamedClassSymbol? - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (this is KtEnumEntry || nameIdentifier == null) { return null } - return KaFe10PsiNamedClassSymbol(this, analysisContext) + KaFe10PsiNamedClassSymbol(this, analysisContext) } override val KtPropertyAccessor.symbol: KaPropertyAccessorSymbol - get() = withValidityAssertion { - return if (isGetter) { + get() = createPsiBasedSymbolWithValidityAssertion { + if (isGetter) { KaFe10PsiPropertyGetterSymbol(this, analysisContext) } else { KaFe10PsiPropertySetterSymbol(this, analysisContext) @@ -121,22 +121,22 @@ internal class KaFe10SymbolProvider( } override val KtClassInitializer.symbol: KaClassInitializerSymbol - get() = withValidityAssertion { KaFe10PsiClassInitializerSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiClassInitializerSymbol(this, analysisContext) } override val KtDestructuringDeclarationEntry.symbol: KaVariableSymbol - get() = withValidityAssertion { KaFe10PsiLocalVariableSymbol(this, analysisContext) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiLocalVariableSymbol(this, analysisContext) } override val KtDestructuringDeclaration.symbol: KaDestructuringDeclarationSymbol - get() = withValidityAssertion { KaFe10PsiDestructuringDeclarationSymbol(this, analysisSession) } + get() = createPsiBasedSymbolWithValidityAssertion { KaFe10PsiDestructuringDeclarationSymbol(this, analysisSession) } override fun findClass(classId: ClassId): KaClassSymbol? = withValidityAssertion { val descriptor = analysisContext.resolveSession.moduleDescriptor.findClassAcrossModuleDependencies(classId) ?: return null - return descriptor.toKaClassSymbol(analysisContext) + descriptor.toKaClassSymbol(analysisContext) } override fun findTypeAlias(classId: ClassId): KaTypeAliasSymbol? = withValidityAssertion { val descriptor = analysisContext.resolveSession.moduleDescriptor.findTypeAliasAcrossModuleDependencies(classId) ?: return null - return descriptor.toKtClassifierSymbol(analysisContext) as? KaTypeAliasSymbol + descriptor.toKtClassifierSymbol(analysisContext) as? KaTypeAliasSymbol } override fun findClassLike(classId: ClassId): KaClassLikeSymbol? = withValidityAssertion { @@ -146,7 +146,7 @@ internal class KaFe10SymbolProvider( override fun findTopLevelCallables(packageFqName: FqName, name: Name): Sequence = withValidityAssertion { val packageViewDescriptor = analysisContext.resolveSession.moduleDescriptor.getPackage(packageFqName) - return packageViewDescriptor.memberScope.getContributedDescriptors(DescriptorKindFilter.ALL, nameFilter = { it == name }) + packageViewDescriptor.memberScope.getContributedDescriptors(DescriptorKindFilter.ALL, nameFilter = { it == name }) .asSequence() .filter { it.name == name } .mapNotNull { it.toKtSymbol(analysisContext) as? KaCallableSymbol } @@ -154,6 +154,6 @@ internal class KaFe10SymbolProvider( override fun findPackage(fqName: FqName): KaPackageSymbol? = withValidityAssertion { if (analysisContext.resolveSession.packageFragmentProvider.isEmpty(fqName)) return null - return KaFe10PackageSymbol(fqName, analysisContext) + KaFe10PackageSymbol(fqName, analysisContext) } } \ No newline at end of file diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KaFirSymbolProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KaFirSymbolProvider.kt index 4e31f743f4e6c..b11de7ba56218 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KaFirSymbolProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KaFirSymbolProvider.kt @@ -27,7 +27,7 @@ internal class KaFirSymbolProvider( private val firSymbolProvider: FirSymbolProvider, ) : KaBaseSymbolProvider(), KaFirSessionComponent { override val KtParameter.symbol: KaVariableSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { when { isFunctionTypeParameter -> errorWithFirSpecificEntries( "Creating ${KaVariableSymbol::class.simpleName} for function type parameter is not possible. " + @@ -41,17 +41,17 @@ internal class KaFirSymbolProvider( } override val KtFile.symbol: KaFileSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirFileSymbol(this, analysisSession) } override val KtScript.symbol: KaScriptSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirScriptSymbol(this, analysisSession) } override val KtNamedFunction.symbol: KaFunctionSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (isAnonymous) { KaFirAnonymousFunctionSymbol(this, analysisSession) } else { @@ -60,32 +60,32 @@ internal class KaFirSymbolProvider( } override val KtConstructor<*>.symbol: KaConstructorSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirConstructorSymbol(this, analysisSession) } override val KtTypeParameter.symbol: KaTypeParameterSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirTypeParameterSymbol(this, analysisSession) } override val KtTypeAlias.symbol: KaTypeAliasSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirTypeAliasSymbol(this, analysisSession) } override val KtEnumEntry.symbol: KaEnumEntrySymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirEnumEntrySymbol(this, analysisSession) } override val KtFunctionLiteral.symbol: KaAnonymousFunctionSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirAnonymousFunctionSymbol(this, analysisSession) } override val KtProperty.symbol: KaVariableSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (isLocal) { KaFirLocalVariableSymbol(this, analysisSession) } else { @@ -94,12 +94,12 @@ internal class KaFirSymbolProvider( } override val KtObjectLiteralExpression.symbol: KaAnonymousObjectSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirAnonymousObjectSymbol(objectDeclaration, analysisSession) } override val KtObjectDeclaration.symbol: KaClassSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (isObjectLiteral()) { KaFirAnonymousObjectSymbol(this, analysisSession) } else { @@ -108,7 +108,7 @@ internal class KaFirSymbolProvider( } override val KtClassOrObject.classSymbol: KaClassSymbol? - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { when (this) { is KtEnumEntry -> null is KtObjectDeclaration -> symbol @@ -117,7 +117,7 @@ internal class KaFirSymbolProvider( } override val KtClassOrObject.namedClassSymbol: KaNamedClassSymbol? - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (this is KtEnumEntry || this.isObjectLiteral()) { return null } @@ -126,7 +126,7 @@ internal class KaFirSymbolProvider( } override val KtPropertyAccessor.symbol: KaPropertyAccessorSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { if (isGetter) { KaFirPropertyGetterSymbol.create(this, analysisSession) } else { @@ -135,12 +135,12 @@ internal class KaFirSymbolProvider( } override val KtClassInitializer.symbol: KaClassInitializerSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirClassInitializerSymbol(this, analysisSession) } override val KtDestructuringDeclarationEntry.symbol: KaVariableSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { when (val parent = parent) { is KtDestructuringDeclaration -> { if (parent.parent?.parent is KtScript) { @@ -162,18 +162,18 @@ internal class KaFirSymbolProvider( } override val KtDestructuringDeclaration.symbol: KaDestructuringDeclarationSymbol - get() = withValidityAssertion { + get() = createPsiBasedSymbolWithValidityAssertion { KaFirDestructuringDeclarationSymbol(this, analysisSession) } override fun findClass(classId: ClassId): KaClassSymbol? = withValidityAssertion { val symbol = firSymbolProvider.getClassLikeSymbolByClassId(classId) as? FirRegularClassSymbol ?: return null - return firSymbolBuilder.classifierBuilder.buildNamedClassSymbol(symbol) + firSymbolBuilder.classifierBuilder.buildNamedClassSymbol(symbol) } override fun findTypeAlias(classId: ClassId): KaTypeAliasSymbol? = withValidityAssertion { val symbol = firSymbolProvider.getClassLikeSymbolByClassId(classId) as? FirTypeAliasSymbol ?: return null - return firSymbolBuilder.classifierBuilder.buildTypeAliasSymbol(symbol) + firSymbolBuilder.classifierBuilder.buildTypeAliasSymbol(symbol) } override fun findClassLike(classId: ClassId): KaClassLikeSymbol? { @@ -183,7 +183,7 @@ internal class KaFirSymbolProvider( override fun findTopLevelCallables(packageFqName: FqName, name: Name): Sequence = withValidityAssertion { val firs = firSymbolProvider.getTopLevelCallableSymbols(packageFqName, name) - return firs.asSequence().map { firSymbol -> + firs.asSequence().map { firSymbol -> firSymbolBuilder.buildSymbol(firSymbol) as KaCallableSymbol } } diff --git a/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml b/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml index c578888cbda0b..d9797821dfdbe 100644 --- a/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml +++ b/analysis/analysis-api-impl-base/resources/META-INF/analysis-api/analysis-api-impl-base.xml @@ -52,5 +52,12 @@ key="kotlin.decompiled.light.classes.check.inconsistency" restartRequired="false" /> + + \ No newline at end of file diff --git a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/KaBaseSymbolProvider.kt b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/KaBaseSymbolProvider.kt index dc19e1bb5461e..7b35b2e0836d5 100644 --- a/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/KaBaseSymbolProvider.kt +++ b/analysis/analysis-api-impl-base/src/org/jetbrains/kotlin/analysis/api/impl/base/components/KaBaseSymbolProvider.kt @@ -5,28 +5,19 @@ package org.jetbrains.kotlin.analysis.api.impl.base.components +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.analysis.api.KaImplementationDetail import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.getModule import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolProvider -import org.jetbrains.kotlin.psi.KtClassInitializer -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtConstructor -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtDestructuringDeclaration -import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry -import org.jetbrains.kotlin.psi.KtEnumEntry -import org.jetbrains.kotlin.psi.KtFunctionLiteral -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtObjectDeclaration -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.KtPropertyAccessor -import org.jetbrains.kotlin.psi.KtScript -import org.jetbrains.kotlin.psi.KtScriptInitializer -import org.jetbrains.kotlin.psi.KtTypeAlias -import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.analysis.api.utils.errors.withKaModuleEntry +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.exceptions.KotlinIllegalArgumentExceptionWithAttachments +import org.jetbrains.kotlin.utils.exceptions.buildAttachment +import org.jetbrains.kotlin.utils.exceptions.withPsiEntry @KaImplementationDetail abstract class KaBaseSymbolProvider : KaSessionComponent(), KaSymbolProvider { @@ -52,4 +43,37 @@ abstract class KaBaseSymbolProvider : KaSessionComponent(), Ka else -> error("Cannot build symbol for ${this::class}") } } + + protected inline fun T.createPsiBasedSymbolWithValidityAssertion(builder: () -> R): R = withValidityAssertion { + with(analysisSession) { + if (!canBeAnalysed() && !Registry.`is`("kotlin.analysis.unrelatedSymbolCreation.allowed", false)) { + throw KaBaseIllegalPsiException(this, this@createPsiBasedSymbolWithValidityAssertion) + } + } + + builder() + } + + @KaImplementationDetail + class KaBaseIllegalPsiException(session: KaSession, psi: PsiElement) : KotlinIllegalArgumentExceptionWithAttachments( + "The element cannot be analyzed in the context of the current session.\n" + + "The call site should be adjusted according to ${KaSymbolProvider::class.simpleName} KDoc." + ) { + init { + with(session) { + buildAttachment("info.txt") { + withKaModuleEntry("useSiteModule", useSiteModule) + + val psiModule = getModule(psi) + withKaModuleEntry("psiModule", psiModule) + + runCatching { + withPsiEntry("psi", psi) + }.exceptionOrNull()?.let { + withEntry("psiException", it.stackTraceToString()) + } + } + } + } + } } diff --git a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/symbols/AbstractSymbolTest.kt b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/symbols/AbstractSymbolTest.kt index acd2f55def26c..2eb0ef2d8a76a 100644 --- a/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/symbols/AbstractSymbolTest.kt +++ b/analysis/analysis-api-impl-base/tests/org/jetbrains/kotlin/analysis/api/impl/base/test/cases/symbols/AbstractSymbolTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols import com.intellij.psi.PsiElement import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.impl.base.components.KaBaseSymbolProvider import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.SymbolTestDirectives.DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.SymbolTestDirectives.DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE_K1 import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.SymbolTestDirectives.DO_NOT_CHECK_NON_PSI_SYMBOL_RESTORE_K2 @@ -35,9 +36,11 @@ import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.assertions +import org.jetbrains.kotlin.test.services.moduleStructure import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.mapToSetOrEmpty import org.opentest4j.AssertionFailedError +import java.util.concurrent.ExecutionException import kotlin.reflect.full.* import kotlin.reflect.jvm.javaField import kotlin.test.fail @@ -63,8 +66,13 @@ abstract class AbstractSymbolTest : AbstractAnalysisApiBasedTest() { abstract fun KaSession.collectSymbols(ktFile: KtFile, testServices: TestServices): SymbolsData override fun doTestByMainFile(mainFile: KtFile, mainModule: KtTestModule, testServices: TestServices) { - doTestByMainFile(mainFile, mainModule, testServices, disablePsiBasedLogic = false) - doTestByMainFile(mainFile, mainModule, testServices, disablePsiBasedLogic = true) + testServices.moduleStructure.allDirectives.suppressIf( + suppressionDirective = SymbolTestDirectives.ILLEGAL_PSI, + filter = Throwable::isIllegalPsiException, + ) { + doTestByMainFile(mainFile, mainModule, testServices, disablePsiBasedLogic = false) + doTestByMainFile(mainFile, mainModule, testServices, disablePsiBasedLogic = true) + } } private fun doTestByMainFile( @@ -181,7 +189,9 @@ abstract class AbstractSymbolTest : AbstractAnalysisApiBasedTest() { } private fun KaSession.checkContainingFiles(symbols: List, mainFile: KtFile, testServices: TestServices) { - val allowedContainingFileSymbols = getAllowedContainingFiles(mainFile, testServices).mapToSetOrEmpty { it.symbol } + val allowedContainingFileSymbols = getAllowedContainingFiles(mainFile, testServices).mapToSetOrEmpty { + it.takeIf { it.canBeAnalysed() }?.symbol + } for (symbol in symbols) { if (symbol.origin != KaSymbolOrigin.SOURCE) continue @@ -373,6 +383,8 @@ object SymbolTestDirectives : SimpleDirectivesContainer() { val PRETTY_RENDERER_OPTION by enumDirective(description = "Explicit rendering mode") { PrettyRendererOption.valueOf(it) } val TARGET_FILE_NAME by stringDirective(description = "The name of the main file") + + val ILLEGAL_PSI by stringDirective(description = "Symbol should not be created for this PSI element") } enum class PrettyRendererOption(val transformation: (KaDeclarationRenderer) -> KaDeclarationRenderer) { @@ -511,3 +523,10 @@ private fun KaSymbol.dropBackingPsi() { // Drop backing PSI to trigger non-psi implementation field.set(this, null) } + +private val Throwable.isIllegalPsiException: Boolean + get() = when (this) { + is KaBaseSymbolProvider.KaBaseIllegalPsiException -> true + is ExecutionException -> cause?.isIllegalPsiException == true + else -> false + } diff --git a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/KaSymbolProvider.kt b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/KaSymbolProvider.kt index b724fa91c33d9..8ac7486985984 100644 --- a/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/KaSymbolProvider.kt +++ b/analysis/analysis-api/src/org/jetbrains/kotlin/analysis/api/symbols/KaSymbolProvider.kt @@ -10,6 +10,15 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +/** + * Provides a mapping between a PSI element and the corresponding [KaSymbol]. + * + * **Note**: symbols can be created only for elements which are a part of the current [KaSession] + * ([KaAnalysisScopeProvider.canBeAnalysed][org.jetbrains.kotlin.analysis.api.components.KaAnalysisScopeProvider.canBeAnalysed] + * is **true** for them). + * + * @see org.jetbrains.kotlin.analysis.api.components.KaAnalysisScopeProvider + */ public interface KaSymbolProvider { public val KtDeclaration.symbol: KaDeclarationSymbol diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.kt b/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.kt index 7f5f4413627e5..4610fc9f01a54 100644 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.kt +++ b/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.kt @@ -1,4 +1,4 @@ -// DO_NOT_CHECK_SYMBOL_RESTORE +// ILLEGAL_PSI // MODULE: dep // FILE: dependency.kt class Dep diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.pretty.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.pretty.txt deleted file mode 100644 index e5cb7e0b9bb9b..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.pretty.txt +++ /dev/null @@ -1 +0,0 @@ -class MyClass diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.txt deleted file mode 100644 index b48de669a6963..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/classFromDependentModule.txt +++ /dev/null @@ -1,30 +0,0 @@ -KaNamedClassSymbol: - annotations: [] - classId: MyClass - classKind: CLASS - companionObject: null - compilerVisibility: Public - contextReceivers: [] - isActual: false - isData: false - isExpect: false - isExternal: false - isFun: false - isInline: false - isInner: false - location: TOP_LEVEL - modality: FINAL - name: MyClass - origin: SOURCE - superTypes: [ - KaUsualClassType: - annotations: [] - typeArguments: [] - type: kotlin/Any - ] - typeParameters: [] - visibility: PUBLIC - getContainingFileSymbol: KaFileSymbol(main.kt) - getContainingModule: KaSourceModule "Sources of main" - annotationApplicableTargets: null - deprecationStatus: null diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.kt b/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.kt index a35fee6bec204..55faf2b731a87 100644 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.kt +++ b/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.kt @@ -1,4 +1,4 @@ -// DO_NOT_CHECK_SYMBOL_RESTORE +// ILLEGAL_PSI // MODULE: dep // FILE: dependency.kt class Dep diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.pretty.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.pretty.txt deleted file mode 100644 index 15079b66c5a4b..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.pretty.txt +++ /dev/null @@ -1 +0,0 @@ -fun main() diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.txt deleted file mode 100644 index f962ed53af619..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/functionFromDependentModule.txt +++ /dev/null @@ -1,35 +0,0 @@ -KaNamedFunctionSymbol: - annotations: [] - callableId: /main - compilerVisibility: Public - contextReceivers: [] - contractEffects: [] - hasStableParameterNames: true - isActual: false - isBuiltinFunctionInvoke: false - isExpect: false - isExtension: false - isExternal: false - isInfix: false - isInline: false - isOperator: false - isOverride: false - isStatic: false - isSuspend: false - isTailRec: false - location: TOP_LEVEL - modality: FINAL - name: main - origin: SOURCE - receiverParameter: null - returnType: KaUsualClassType: - annotations: [] - typeArguments: [] - type: kotlin/Unit - typeParameters: [] - valueParameters: [] - visibility: PUBLIC - getContainingFileSymbol: KaFileSymbol(main.kt) - getContainingJvmClassName: MainKt - getContainingModule: KaSourceModule "Sources of main" - deprecationStatus: null diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.kt b/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.kt index 8fdb367b10e3a..1687be8a1c6c3 100644 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.kt +++ b/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.kt @@ -1,4 +1,4 @@ -// DO_NOT_CHECK_SYMBOL_RESTORE +// ILLEGAL_PSI // MODULE: dep // FILE: dependency.kt class Dep diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.pretty.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.pretty.txt deleted file mode 100644 index 643630bcbdb3b..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.pretty.txt +++ /dev/null @@ -1 +0,0 @@ -val prop: kotlin.Int diff --git a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.txt b/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.txt deleted file mode 100644 index f35ee9144aa45..0000000000000 --- a/analysis/analysis-api/testData/symbols/symbolByPsi/propertyFromDependentModule.txt +++ /dev/null @@ -1,90 +0,0 @@ -KaKotlinPropertySymbol: - annotations: [] - backingFieldSymbol: KaBackingFieldSymbol: - annotations: [] - callableId: null - compilerVisibility: Private - contextReceivers: [] - isActual: false - isExpect: false - isExtension: false - isVal: true - location: PROPERTY - modality: FINAL - name: field - origin: PROPERTY_BACKING_FIELD - owningProperty: KaKotlinPropertySymbol(/prop) - receiverParameter: null - returnType: KaUsualClassType: - annotations: [] - typeArguments: [] - type: kotlin/Int - visibility: PRIVATE - getContainingFileSymbol: KaFileSymbol(main.kt) - getContainingJvmClassName: MainKt - getContainingModule: KaSourceModule "Sources of main" - deprecationStatus: null - callableId: /prop - compilerVisibility: Public - contextReceivers: [] - getter: KaPropertyGetterSymbol: - annotations: [] - callableId: null - compilerVisibility: Public - contextReceivers: [] - hasBody: false - hasStableParameterNames: true - isActual: false - isDefault: true - isExpect: false - isExtension: false - isInline: false - isOverride: false - location: PROPERTY - modality: FINAL - origin: SOURCE - receiverParameter: null - returnType: KaUsualClassType: - annotations: [] - typeArguments: [] - type: kotlin/Int - valueParameters: [] - visibility: PUBLIC - getContainingFileSymbol: KaFileSymbol(main.kt) - getContainingJvmClassName: MainKt - getContainingModule: KaSourceModule "Sources of main" - deprecationStatus: null - hasBackingField: true - hasGetter: true - hasSetter: false - initializer: KtConstantInitializerValue(0) - isActual: false - isConst: false - isDelegatedProperty: false - isExpect: false - isExtension: false - isFromPrimaryConstructor: false - isLateInit: false - isOverride: false - isStatic: false - isVal: true - location: TOP_LEVEL - modality: FINAL - name: prop - origin: SOURCE - receiverParameter: null - returnType: KaUsualClassType: - annotations: [] - typeArguments: [] - type: kotlin/Int - setter: null - typeParameters: [] - visibility: PUBLIC - getContainingFileSymbol: KaFileSymbol(main.kt) - getContainingJvmClassName: MainKt - getContainingModule: KaSourceModule "Sources of main" - deprecationStatus: null - getterDeprecationStatus: null - javaGetterName: getProp - javaSetterName: null - setterDeprecationStatus: null diff --git a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/base/AbstractAnalysisApiBasedTest.kt b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/base/AbstractAnalysisApiBasedTest.kt index ffba74deb703e..38c456150c299 100644 --- a/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/base/AbstractAnalysisApiBasedTest.kt +++ b/analysis/analysis-test-framework/tests/org/jetbrains/kotlin/analysis/test/framework/base/AbstractAnalysisApiBasedTest.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.test.builders.testConfiguration import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.directives.model.Directive import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives +import org.jetbrains.kotlin.test.directives.model.StringDirective import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue import org.jetbrains.kotlin.test.model.DependencyKind import org.jetbrains.kotlin.test.model.FrontendKinds @@ -440,4 +441,24 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() { tags = testInfo.tags ) } + + fun RegisteredDirectives.suppressIf(suppressionDirective: StringDirective, filter: (Throwable) -> Boolean, action: () -> Unit) { + val hasSuppressionDirective = suppressionDirective in this + var exception: Throwable? = null + try { + action() + } catch (e: Throwable) { + exception = e + } + + if (exception != null) { + if (!filter(exception) || !hasSuppressionDirective) { + throw exception + } + + return + } else if (hasSuppressionDirective) { + throw AssertionError("'${suppressionDirective.name}' directive present but no exception thrown. Please remove directive") + } + } } \ No newline at end of file