Skip to content

Commit

Permalink
[Analysis API] forbid symbol creation for unrelated declarations
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dimonchik0036 authored and qodana-bot committed Nov 22, 2024
1 parent 6636dc1 commit da0d1c0
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -50,51 +50,51 @@ 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)
}
}

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)
}
}

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
KaFe10PsiNamedClassSymbol(this, analysisContext)
}

override val KtClassOrObject.classSymbol: KaClassSymbol?
get() = withValidityAssertion {
get() = createPsiBasedSymbolWithValidityAssertion {
when (this) {
is KtEnumEntry -> null
is KtObjectDeclaration -> symbol
Expand All @@ -103,40 +103,40 @@ 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)
}
}

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 {
Expand All @@ -146,14 +146,14 @@ internal class KaFe10SymbolProvider(

override fun findTopLevelCallables(packageFqName: FqName, name: Name): Sequence<KaCallableSymbol> = 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 }
}

override fun findPackage(fqName: FqName): KaPackageSymbol? = withValidityAssertion {
if (analysisContext.resolveSession.packageFragmentProvider.isEmpty(fqName)) return null
return KaFe10PackageSymbol(fqName, analysisContext)
KaFe10PackageSymbol(fqName, analysisContext)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal class KaFirSymbolProvider(
private val firSymbolProvider: FirSymbolProvider,
) : KaBaseSymbolProvider<KaFirSession>(), KaFirSessionComponent {
override val KtParameter.symbol: KaVariableSymbol
get() = withValidityAssertion {
get() = createPsiBasedSymbolWithValidityAssertion {
when {
isFunctionTypeParameter -> errorWithFirSpecificEntries(
"Creating ${KaVariableSymbol::class.simpleName} for function type parameter is not possible. " +
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -108,7 +108,7 @@ internal class KaFirSymbolProvider(
}

override val KtClassOrObject.classSymbol: KaClassSymbol?
get() = withValidityAssertion {
get() = createPsiBasedSymbolWithValidityAssertion {
when (this) {
is KtEnumEntry -> null
is KtObjectDeclaration -> symbol
Expand All @@ -117,7 +117,7 @@ internal class KaFirSymbolProvider(
}

override val KtClassOrObject.namedClassSymbol: KaNamedClassSymbol?
get() = withValidityAssertion {
get() = createPsiBasedSymbolWithValidityAssertion {
if (this is KtEnumEntry || this.isObjectLiteral()) {
return null
}
Expand All @@ -126,7 +126,7 @@ internal class KaFirSymbolProvider(
}

override val KtPropertyAccessor.symbol: KaPropertyAccessorSymbol
get() = withValidityAssertion {
get() = createPsiBasedSymbolWithValidityAssertion {
if (isGetter) {
KaFirPropertyGetterSymbol.create(this, analysisSession)
} else {
Expand All @@ -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) {
Expand All @@ -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? {
Expand All @@ -183,7 +183,7 @@ internal class KaFirSymbolProvider(

override fun findTopLevelCallables(packageFqName: FqName, name: Name): Sequence<KaCallableSymbol> = withValidityAssertion {
val firs = firSymbolProvider.getTopLevelCallableSymbols(packageFqName, name)
return firs.asSequence().map { firSymbol ->
firs.asSequence().map { firSymbol ->
firSymbolBuilder.buildSymbol(firSymbol) as KaCallableSymbol
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@
key="kotlin.decompiled.light.classes.check.inconsistency"
restartRequired="false"
/>

<registryKey
defaultValue="false"
description="Allow a KaSymbol creation in an incorrect context"
key="kotlin.analysis.unrelatedSymbolCreation.allowed"
restartRequired="false"
/>
</extensions>
</idea-plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -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<T : KaSession> : KaSessionComponent<T>(), KaSymbolProvider {
Expand All @@ -52,4 +43,37 @@ abstract class KaBaseSymbolProvider<T : KaSession> : KaSessionComponent<T>(), Ka
else -> error("Cannot build symbol for ${this::class}")
}
}

protected inline fun <T : PsiElement, R> 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())
}
}
}
}
}
}
Loading

0 comments on commit da0d1c0

Please sign in to comment.