From 827c262f682eb9e9b53b5af77290b85f852f75c2 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Sat, 23 Jul 2022 15:59:30 +0200 Subject: [PATCH] [Analysis API FIR, LL FIR] use exceptionWithAttachment to not leak user data --- .../api/fir/KtFirAnalysisSessionProvider.kt | 7 +- .../analysis/api/fir/KtSymbolByFirBuilder.kt | 28 ++++++- .../api/fir/components/KtFirCallResolver.kt | 3 +- ...tFirSymbolContainingDeclarationProvider.kt | 42 +++++++--- .../analysis/api/fir/symbols/KtFirSymbol.kt | 30 ++++--- .../api/fir/symbols/KtFirSymbolProvider.kt | 19 ++++- .../api/fir/types/KtFirFunctionalType.kt | 3 +- .../api/fir/types/KtFirTypeParameterType.kt | 3 +- .../api/fir/types/KtFirUsualClassType.kt | 3 +- .../analysis/api/fir/utils/errorUtils.kt | 26 ++++++ .../kotlin/analysis/utils/errors/errors.kt | 6 +- .../low/level/api/fir/FirDesignationState.kt | 3 +- .../level/api/fir/LLFirResolveStateService.kt | 5 +- .../api/fir/api/FirDeclarationDesignation.kt | 22 +++-- .../fir/api/InvalidFirElementTypeException.kt | 27 +++++-- ...arationAndFirDeclarationEqualityChecker.kt | 7 +- .../LowLevelFirApiFacadeForResolveOnAir.kt | 19 ++++- .../LLFirFirClassByPsiClassProvider.kt | 9 ++- .../state/LLFirResolvableResolveSession.kt | 12 ++- ...irDeclarationForCompiledElementSearcher.kt | 30 ++++--- .../api/fir/util/contrainingFileUtils.kt | 2 +- .../level/api/fir/util/declarationUtils.kt | 8 +- .../low/level/api/fir/util/exceptionUtils.kt | 80 +++++++++++++++++++ .../analysis/low/level/api/fir/util/utils.kt | 13 ++- .../utils/KotlinExceptionWithAttachments.kt | 23 +++++- 25 files changed, 351 insertions(+), 79 deletions(-) create mode 100644 analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/errorUtils.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/exceptionUtils.kt diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt index dd7ec338b82..5d54a6f9652 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtFirAnalysisSessionProvider.kt @@ -9,13 +9,16 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol +import org.jetbrains.kotlin.analysis.api.fir.utils.withSymbolAttachment import org.jetbrains.kotlin.analysis.api.impl.base.CachingKtAnalysisSessionProvider import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.api.lifetime.KtLifetimeToken import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.utils.errorWithAttachment @OptIn(KtAnalysisApiInternals::class) class KtFirAnalysisSessionProvider(project: Project) : CachingKtAnalysisSessionProvider(project) { @@ -26,7 +29,9 @@ class KtFirAnalysisSessionProvider(project: Project) : CachingKtAnalysisSessionP override fun getFirResolveSession(contextSymbol: KtSymbol): LLFirResolveSession { return when (contextSymbol) { is KtFirSymbol<*> -> contextSymbol.firResolveSession - else -> error("Invalid symbol ${contextSymbol::class}") + else -> errorWithAttachment("Invalid symbol") { + withSymbolAttachment("symbol", contextSymbol) + } } } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt index 355ff247dae..438da8d85b7 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/KtSymbolByFirBuilder.kt @@ -20,6 +20,10 @@ import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withConeTypeAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirSymbolAttachment import org.jetbrains.kotlin.analysis.providers.createPackageProvider import org.jetbrains.kotlin.builtins.functions.FunctionClassKind import org.jetbrains.kotlin.fir.* @@ -49,6 +53,7 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.utils.errorWithAttachment import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -203,7 +208,10 @@ internal class KtSymbolByFirBuilder constructor( return buildFunctionSymbol(it.symbol) } if (firSymbol.dispatchReceiverType?.contains { it is ConeStubType } == true) { - return buildFunctionSymbol(firSymbol.originalIfFakeOverride() ?: error("Stub type in real declaration")) + return buildFunctionSymbol( + firSymbol.originalIfFakeOverride() + ?: firErrorWithAttachment("Stub type in real declaration", fir = firSymbol.fir) + ) } check(firSymbol.origin != FirDeclarationOrigin.SamConstructor) @@ -579,8 +587,22 @@ internal class KtSymbolByFirBuilder constructor( } companion object { - private fun throwUnexpectedElementError(element: Any): Nothing { - error("Unexpected ${element::class.simpleName}") + private fun throwUnexpectedElementError(element: FirBasedSymbol<*>): Nothing { + errorWithAttachment("Unexpected ${element::class.simpleName}") { + withFirSymbolAttachment("firSymbol", element) + } + } + + private fun throwUnexpectedElementError(element: FirElement): Nothing { + errorWithAttachment("Unexpected ${element::class.simpleName}") { + withFirAttachment("firElement", element) + } + } + + private fun throwUnexpectedElementError(element: ConeKotlinType): Nothing { + errorWithAttachment("Unexpected ${element::class.simpleName}") { + withConeTypeAttachment("coneType", element) + } } @OptIn(ExperimentalContracts::class) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt index 3dbce599509..ebb72a99e21 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCallResolver.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.AllCandidatesResolver +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol @@ -151,7 +152,7 @@ internal class KtFirCallResolver( when (val calleeReference = calleeReference) { is FirResolvedNamedReference -> { val call = createKtCall(psi, this, null, resolveFragmentOfCall) - ?: error("expect `createKtCall` to succeed for resolvable case") + ?: firErrorWithAttachment("expect `createKtCall` to succeed for resolvable case", fir = this, psi = psi) KtSuccessCallInfo(call) } is FirErrorNamedReference -> { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt index 04769dbab4f..1fb6571ca0b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirSymbolContainingDeclarationProvider.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirReceiverParameterSymbo import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirTypeParameterSymbol import org.jetbrains.kotlin.analysis.api.fir.utils.getContainingKtModule +import org.jetbrains.kotlin.analysis.api.fir.utils.withSymbolAttachment import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolKind import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithKind @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.psi import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.errorWithAttachment internal class KtFirSymbolContainingDeclarationProvider( override val analysisSession: KtFirAnalysisSession, @@ -85,21 +87,33 @@ internal class KtFirSymbolContainingDeclarationProvider( private fun getContainingPsi(symbol: KtFirSymbol<*>): KtDeclaration { val source = symbol.firSymbol.source val thisSource = when (source?.kind) { - null -> error("PSI should present for declaration built by Kotlin code") + null -> errorWithAttachment("PSI should present for declaration built by Kotlin code") { + withSymbolAttachment("symbolForContainingPsi", symbol) + } + KtFakeSourceElementKind.ImplicitConstructor -> return source.psi as KtDeclaration + KtFakeSourceElementKind.PropertyFromParameter -> return source.psi?.parentOfType()!! KtFakeSourceElementKind.DefaultAccessor -> return source.psi as KtProperty KtFakeSourceElementKind.ItLambdaParameter -> return source.psi as KtFunctionLiteral KtRealSourceElementKind -> source.psi!! - else -> error("Unexpected FirSourceElement: kind=${source.kind} element=${source.psi!!::class.simpleName}") + else -> + errorWithAttachment("errorWithAttachment FirSourceElement: kind=${source.kind} element=${source.psi!!::class.simpleName}") { + withSymbolAttachment("symbolForContainingPsi", symbol) + } } return when (symbol.origin) { KtSymbolOrigin.SOURCE -> thisSource.getContainingKtDeclaration() - ?: error("Containing declaration should present for non-toplevel declaration") + ?: errorWithAttachment("Containing declaration should present for non-toplevel declaration") { + withSymbolAttachment("symbolForContainingPsi", symbol) + } + KtSymbolOrigin.SOURCE_MEMBER_GENERATED -> thisSource as KtDeclaration - else -> error("Unsupported declaration origin ${symbol.origin}") + else -> errorWithAttachment("Unsupported declaration origin ${symbol.origin}") { + withSymbolAttachment("symbolForContainingPsi", symbol) + } } } @@ -113,21 +127,31 @@ internal class KtFirSymbolContainingDeclarationProvider( require(symbol.origin == KtSymbolOrigin.LIBRARY || symbol.origin == KtSymbolOrigin.JAVA) check(symbol.symbolKind == KtSymbolKind.CLASS_MEMBER) + fun error(message: String): Nothing { + errorWithAttachment(message) { + withSymbolAttachment("symbolToGetContaining", symbol) + } + } + val containingClassId = when (symbol) { is KtClassLikeSymbol -> { - val classId = symbol.classIdIfNonLocal ?: error("classId should not be null for non-local declaration") + val classId = symbol.classIdIfNonLocal + ?: error("classId should not be null for non-local declaration") classId.outerClassId } is KtFunctionSymbol -> { - val fqName = symbol.callableIdIfNonLocal ?: error("callableIdIfNonLocal should not be null for non-local declaration") + val fqName = symbol.callableIdIfNonLocal + ?: error("callableIdIfNonLocal should not be null for non-local declaration") fqName.classId } is KtEnumEntrySymbol -> { - val classId = symbol.containingEnumClassIdIfNonLocal ?: error("fqName should not be null for non-local declaration") + val classId = symbol.containingEnumClassIdIfNonLocal + ?: error("fqName should not be null for non-local declaration") classId.outerClassId } is KtPropertySymbol -> { - val fqName = symbol.callableIdIfNonLocal ?: error("fqName should not be null for non-local declaration") + val fqName = symbol.callableIdIfNonLocal + ?: error("fqName should not be null for non-local declaration") fqName.classId } is KtConstructorSymbol -> { @@ -137,6 +161,6 @@ internal class KtFirSymbolContainingDeclarationProvider( else -> error("We should not have a ${symbol::class} from a library") } ?: error("outerClassId should not be null for member declaration") val containingClass = containingClassId.getCorrespondingToplevelClassOrObjectSymbol() - return containingClass ?: error("Class with id $containingClassId should exists") + return containingClass ?: error("Class by class id should exists") } } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt index 35df5d5a069..f02acaa160b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbol.kt @@ -11,17 +11,18 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol import org.jetbrains.kotlin.analysis.api.symbols.KtSymbolOrigin import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirAttachment import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticPropertyAccessor -import org.jetbrains.kotlin.fir.originalIfFakeOverride -import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData import org.jetbrains.kotlin.fir.scopes.impl.originalForWrappedIntegerOperator import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.utils.errorWithAttachment internal interface KtFirSymbol> : KtSymbol, KtLifetimeOwner { val firSymbol: S @@ -49,6 +50,7 @@ internal tailrec fun FirDeclaration.ktSymbolOrigin(): KtSymbolOrigin = when (ori else -> KtSymbolOrigin.SOURCE } } + FirDeclarationOrigin.Precompiled -> KtSymbolOrigin.SOURCE FirDeclarationOrigin.Library, FirDeclarationOrigin.BuiltIns -> KtSymbolOrigin.LIBRARY is FirDeclarationOrigin.Java -> KtSymbolOrigin.JAVA @@ -60,26 +62,36 @@ internal tailrec fun FirDeclaration.ktSymbolOrigin(): KtSymbolOrigin = when (ori when (this) { is FirSyntheticProperty, is FirSyntheticPropertyAccessor -> KtSymbolOrigin.JAVA_SYNTHETIC_PROPERTY - else -> throw InvalidFirDeclarationOriginForSymbol(this) + + else -> errorWithAttachment("Invalid FirDeclarationOrigin ${origin::class.simpleName}") { + withFirAttachment("firToGetOrigin", this@ktSymbolOrigin) + } } } + FirDeclarationOrigin.ImportedFromObject -> { val importedFromObjectData = (this as FirCallableDeclaration).importedFromObjectData - ?: error("Declaration has ImportedFromObject origin, but no importedFromObjectData present") + ?: errorWithAttachment("Declaration has ImportedFromObject origin, but no importedFromObjectData present") { + withFirAttachment("firToGetOrigin", this@ktSymbolOrigin) + } importedFromObjectData.original.ktSymbolOrigin() } + FirDeclarationOrigin.WrappedIntegerOperator -> { val original = (this as FirSimpleFunction).originalForWrappedIntegerOperator?.fir - ?: error("Declaration has WrappedIntegerOperator origin, but no originalForWrappedIntegerOperator present") + ?: firErrorWithAttachment( + "Declaration has WrappedIntegerOperator origin, but no originalForWrappedIntegerOperator present", + fir = this + ) original.ktSymbolOrigin() } + is FirDeclarationOrigin.Plugin -> KtSymbolOrigin.PLUGIN FirDeclarationOrigin.RenamedForOverride -> KtSymbolOrigin.JAVA FirDeclarationOrigin.SubstitutionOverride -> KtSymbolOrigin.SUBSTITUTION_OVERRIDE - FirDeclarationOrigin.DynamicScope -> throw InvalidFirDeclarationOriginForSymbol(this) + FirDeclarationOrigin.DynamicScope -> errorWithAttachment("Invalid FirDeclarationOrigin ${origin::class.simpleName}") { + withFirAttachment("firToGetOrigin", this@ktSymbolOrigin) + } } - -class InvalidFirDeclarationOriginForSymbol(declaration: FirDeclaration) : - IllegalStateException("Invalid FirDeclarationOrigin ${declaration.origin::class.simpleName} for ${declaration.render()}") diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbolProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbolProvider.kt index b6aba5ca030..f9b0d0a6578 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbolProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/KtFirSymbolProvider.kt @@ -10,6 +10,9 @@ import org.jetbrains.kotlin.analysis.api.fir.components.KtFirAnalysisSessionComp import org.jetbrains.kotlin.analysis.api.symbols.* import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbolOfType +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirSymbolAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withPsiAttachment import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass import org.jetbrains.kotlin.fir.render @@ -20,6 +23,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.errorWithAttachment internal class KtFirSymbolProvider( override val analysisSession: KtFirAnalysisSession, @@ -28,8 +32,9 @@ internal class KtFirSymbolProvider( override fun getParameterSymbol(psi: KtParameter): KtVariableLikeSymbol { return when { - psi.isFunctionTypeParameter -> error( - "Creating KtValueParameterSymbol for function type parameter is not possible. Please see the KDoc of getParameterSymbol" + psi.isFunctionTypeParameter -> firErrorWithAttachment( + "Creating KtValueParameterSymbol for function type parameter is not possible. Please see the KDoc of getParameterSymbol", + psi = psi, ) psi.isLoopParameter -> { @@ -63,7 +68,10 @@ internal class KtFirSymbolProvider( } is FirAnonymousFunctionSymbol -> firSymbolBuilder.functionLikeBuilder.buildAnonymousFunctionSymbol(firSymbol) - else -> error("Unexpected ${firSymbol.fir.renderWithType()}") + else -> errorWithAttachment("Unexpected ${firSymbol::class}") { + withFirSymbolAttachment("firSymbol", firSymbol) + withPsiAttachment("function", psi) + } } } @@ -117,7 +125,10 @@ internal class KtFirSymbolProvider( val firClass = when (val firClassLike = psi.resolveToFirSymbolOfType>(firResolveSession)) { is FirTypeAliasSymbol -> firClassLike.fullyExpandedClass(firResolveSession.useSiteFirSession) - ?: error("${firClassLike.fir.render()} should be expanded to the expected type alias") + ?: errorWithAttachment("${firClassLike.fir::class} should be expanded to the expected type alias") { + withFirSymbolAttachment("firClassLikeSymbol", firClassLike) + withPsiAttachment("ktClassOrObject", psi) + } is FirAnonymousObjectSymbol -> firClassLike is FirRegularClassSymbol -> firClassLike } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirFunctionalType.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirFunctionalType.kt index dcd79a398d4..a39c6f94b0b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirFunctionalType.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirFunctionalType.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol import org.jetbrains.kotlin.analysis.api.types.KtFunctionalType import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.isExtensionFunctionType import org.jetbrains.kotlin.fir.types.isSuspendFunctionType @@ -32,7 +33,7 @@ internal class KtFirFunctionalType( override val classId: ClassId get() = withValidityAssertion { coneType.lookupTag.classId } override val classSymbol: KtClassLikeSymbol by cached { builder.classifierBuilder.buildClassLikeSymbolByLookupTag(coneType.lookupTag) - ?: error("Class ${coneType.lookupTag} was not found") + ?: firErrorWithAttachment("Class was not found", coneType = coneType) } override val typeArguments: List by cached { coneType.typeArguments.map { typeArgument -> diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirTypeParameterType.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirTypeParameterType.kt index 3b72d304705..4c7936e1d07 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirTypeParameterType.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirTypeParameterType.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability import org.jetbrains.kotlin.analysis.api.types.KtTypeParameterType +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.fir.types.ConeTypeParameterType import org.jetbrains.kotlin.fir.types.renderForDebugging import org.jetbrains.kotlin.name.Name @@ -26,7 +27,7 @@ internal class KtFirTypeParameterType( override val name: Name get() = withValidityAssertion { coneType.lookupTag.name } override val symbol: KtTypeParameterSymbol by cached { builder.classifierBuilder.buildTypeParameterSymbolByLookupTag(coneType.lookupTag) - ?: error("Type parameter ${coneType.lookupTag} was not found") + ?: firErrorWithAttachment("Type parameter was not found", coneType = coneType) } override val annotationsList: KtAnnotationsList by cached { diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirUsualClassType.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirUsualClassType.kt index 2033362d5da..06ea3552c39 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirUsualClassType.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/types/KtFirUsualClassType.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.analysis.api.lifetime.withValidityAssertion import org.jetbrains.kotlin.analysis.api.symbols.KtClassLikeSymbol import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability import org.jetbrains.kotlin.analysis.api.types.KtUsualClassType +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.renderForDebugging import org.jetbrains.kotlin.name.ClassId @@ -27,7 +28,7 @@ internal class KtFirUsualClassType( override val classId: ClassId get() = withValidityAssertion { coneType.lookupTag.classId } override val classSymbol: KtClassLikeSymbol by cached { builder.classifierBuilder.buildClassLikeSymbolByLookupTag(coneType.lookupTag) - ?: error("Class ${coneType.lookupTag} was not found") + ?: firErrorWithAttachment("Class was not found", coneType = coneType) } override val typeArguments: List by cached { coneType.typeArguments.map { typeArgument -> diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/errorUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/errorUtils.kt new file mode 100644 index 00000000000..7586f229274 --- /dev/null +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/utils/errorUtils.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.analysis.api.fir.utils + +import org.jetbrains.kotlin.analysis.api.fir.symbols.KtFirSymbol +import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer +import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withPsiAttachment +import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments +import org.jetbrains.kotlin.utils.withAttachmentDetailed + + +fun KotlinExceptionWithAttachments.withSymbolAttachment(name: String, symbol: KtSymbol): KotlinExceptionWithAttachments { + withAttachmentDetailed(name, symbol, DebugSymbolRenderer::render) + withPsiAttachment("${name}Psi", symbol.psi) + + if (symbol is KtFirSymbol<*>) { + val symbolFir = symbol.firSymbol.fir + withFirAttachment("${name}Fir", symbolFir) + } + return this +} \ No newline at end of file diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/errors.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/errors.kt index da161a9037f..b8b100fc8c0 100644 --- a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/errors.kt +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/errors.kt @@ -5,8 +5,12 @@ package org.jetbrains.kotlin.analysis.utils.errors +import org.jetbrains.kotlin.utils.errorWithAttachment + public fun unexpectedElementError(elementName: String, element: Any?): Nothing { - error("Unexpected $elementName ${element?.let { it::class.simpleName }}") + errorWithAttachment("Unexpected $elementName ${element?.let { it::class.simpleName }}") { + withAttachment("element", element) + } } public inline fun unexpectedElementError(element: Any?): Nothing { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/FirDesignationState.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/FirDesignationState.kt index f1731a24c9f..ed65095c606 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/FirDesignationState.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/FirDesignationState.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.api.FirDeclarationDesignation +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment abstract class ContextByDesignationCollector(private val designation: FirDeclarationDesignation) { private var context: C? = null @@ -56,7 +57,7 @@ private class FirDesignationState(val designation: FirDeclarationDesignation) { val currentDeclaration: FirDeclaration get() = currentDeclarationIfPresent - ?: error("Went inside target declaration") + ?: firErrorWithAttachment("Went inside target declaration") fun goNext() { if (canGoNext()) { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt index 4a873bb0e04..48e9b925380 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/LLFirResolveStateService.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirLibraryOrLibra import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirNotUnderContentRootResolvableResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirResolvableResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.state.LLFirSourceResolveSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.analysis.providers.createProjectWideOutOfBlockModificationTracker import org.jetbrains.kotlin.analysis.utils.caches.strongCachedValue @@ -86,7 +87,9 @@ internal class LLFirResolveSessionService(project: Project) { } else -> { - error("Unexpected $useSiteKtModule") + firErrorWithAttachment("Unexpected ${useSiteKtModule::class.java}") { + withAttachment("module", useSiteKtModule.moduleDescription) + } } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/FirDeclarationDesignation.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/FirDeclarationDesignation.kt index cff4dda2896..dc1abde4ffc 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/FirDeclarationDesignation.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/FirDeclarationDesignation.kt @@ -5,17 +5,20 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.api +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirAttachment import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.fir.containingClass import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.classId import org.jetbrains.kotlin.fir.declarations.utils.isLocal -import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.renderWithType import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.toFirRegularClass import org.jetbrains.kotlin.fir.symbols.impl.LookupTagInternals +import org.jetbrains.kotlin.utils.checkWithAttachment +import org.jetbrains.kotlin.utils.errorWithAttachment class FirDeclarationDesignationWithFile( path: List, @@ -77,21 +80,28 @@ private fun collectDesignationPath(declaration: FirDeclaration): List return null } ?: return emptyList() - require(containingClass is FirRegularClass) { - "FirRegularClass as containing declaration expected but found ${containingClass.renderWithType()}" + checkWithAttachment(containingClass is FirRegularClass, { "FirRegularClass as containing declaration expected" }) { + it.withFirAttachment("containingClassFir", containingClass) } return if (!containingClass.isLocal) containingClass.collectForNonLocal().asReversed() else null } fun FirDeclaration.collectDesignation(firFile: FirFile): FirDeclarationDesignationWithFile = - tryCollectDesignation(firFile) ?: error("No designation of local declaration ${this.render()}") + tryCollectDesignation(firFile) ?: errorWithAttachment("No designation of local declaration") { + withFirAttachment("firFile", firFile) + } fun FirDeclaration.collectDesignation(): FirDeclarationDesignation = - tryCollectDesignation() ?: error("No designation of local declaration ${this.render()}") + tryCollectDesignation() + ?: errorWithAttachment("No designation of local declaration") { + withFirAttachment("FirDeclaration", this@collectDesignation) + } fun FirDeclaration.collectDesignationWithFile(): FirDeclarationDesignationWithFile = tryCollectDesignationWithFile() - ?: error("No designation of local declaration ${this.render()}") + ?: errorWithAttachment("No designation of local declaration") { + withFirAttachment("FirDeclaration", this@collectDesignationWithFile) + } fun FirDeclaration.tryCollectDesignation(firFile: FirFile): FirDeclarationDesignationWithFile? = collectDesignationPath(this)?.let { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/InvalidFirElementTypeException.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/InvalidFirElementTypeException.kt index f89a013a5f4..68421bde361 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/InvalidFirElementTypeException.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/InvalidFirElementTypeException.kt @@ -5,16 +5,33 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.api +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withConeTypeAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirAttachment +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withFirSymbolAttachment import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments +import org.jetbrains.kotlin.utils.withAttachmentDetailed import java.util.* import kotlin.reflect.KClass class InvalidFirElementTypeException( - actualFirClass: KClass<*>?, + actualFirElement: Any?, ktElement: KtElement?, expectedFirClasses: List>, -) : IllegalStateException() { +) : KotlinExceptionWithAttachments("") { + init { + when (actualFirElement) { + is FirElement -> withFirAttachment("firElement", actualFirElement) + is FirBasedSymbol<*> -> withFirSymbolAttachment("firSymbol", actualFirElement) + is ConeKotlinType -> withConeTypeAttachment("coneType", actualFirElement) + null -> {} + else -> withAttachmentDetailed("element", actualFirElement) { it.toString() } + } + } + override val message: String = buildString { if (ktElement != null) { append("For $ktElement with text `${ktElement.text}`, ") @@ -25,8 +42,8 @@ class InvalidFirElementTypeException( else -> "One of [${expectedFirClasses.joinToString()}] element types expected, but" } append(if (ktElement == null) message else message.replaceFirstChar { it.lowercase(Locale.getDefault()) }) - if (actualFirClass != null) { - append(" ${actualFirClass.simpleName} found") + if (actualFirElement != null) { + append(" ${actualFirElement::class.simpleName} found") } else { append(" no element found") } @@ -39,5 +56,5 @@ fun throwUnexpectedFirElementError( ktElement: KtElement? = null, vararg expectedFirClasses: KClass<*> ): Nothing { - throw InvalidFirElementTypeException(firElement?.let { it::class }, ktElement, expectedFirClasses.toList()) + throw InvalidFirElementTypeException(firElement, ktElement, expectedFirClasses.toList()) } \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/KtDeclarationAndFirDeclarationEqualityChecker.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/KtDeclarationAndFirDeclarationEqualityChecker.kt index 8fc97ff9034..da9d7f2065e 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/KtDeclarationAndFirDeclarationEqualityChecker.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/KtDeclarationAndFirDeclarationEqualityChecker.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.api import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.createEmptySession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.builder.BodyBuildingMode @@ -132,7 +133,7 @@ object KtDeclarationAndFirDeclarationEqualityChecker { } } } - else -> error("Invalid type reference $this") + else -> firErrorWithAttachment("Invalid type reference", fir = this) } return if (isVararg) { rendered.asArrayType() @@ -165,7 +166,7 @@ object KtDeclarationAndFirDeclarationEqualityChecker { } append(typeRef.renderTypeAsKotlinType()) } - else -> error("Invalid type projection $this") + else -> firErrorWithAttachment("Invalid type reference", fir = this) } private fun isTheSameTypes( @@ -200,7 +201,7 @@ object KtDeclarationAndFirDeclarationEqualityChecker { // Can be present as return type "${lowerBound.renderTypeAsKotlinType()}..${upperBound.renderTypeAsKotlinType()}" } - else -> error("Type $this should not be present in Kotlin declaration") + else -> firErrorWithAttachment("Type should not be present in Kotlin declaration", coneType = this) }.replace('/', '.') return rendered + nullability.suffix } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt index 15e59810a8a..2ac667be495 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/api/LowLevelFirApiFacadeForResolveOnAir.kt @@ -18,7 +18,10 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.KtToFirMap import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.* import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableModuleSession import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirResolvableSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.llFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.firErrorWithAttachment import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withPsiAttachment import org.jetbrains.kotlin.analysis.project.structure.getKtModule import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext import org.jetbrains.kotlin.analysis.utils.printer.parentOfType @@ -37,6 +40,7 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.utils.errorWithAttachment object LowLevelFirApiFacadeForResolveOnAir { @@ -87,7 +91,10 @@ object LowLevelFirApiFacadeForResolveOnAir { } declaration.accept(expressionLocator) - return expressionLocator.result ?: error("Resolved on-air element was not found in containing declaration") + return expressionLocator.result ?: firErrorWithAttachment("Resolved on-air element was not found in containing declaration") { + withPsiAttachment("place", place) + withPsiAttachment("elementToResolve", elementToResolve) + } } fun onAirGetTowerContextProvider( @@ -152,7 +159,10 @@ object LowLevelFirApiFacadeForResolveOnAir { val sameDeclarationInOriginalFile = PsiTreeUtil.findSameElementInCopy(dependencyNonLocalDeclaration, originalKtFile) - ?: error("Cannot find original function matching to ${dependencyNonLocalDeclaration.getElementTextInContext()} in $originalKtFile") + ?: firErrorWithAttachment("Cannot find original function matching") { + withPsiAttachment("matchingPsi", dependencyNonLocalDeclaration) + withPsiAttachment("originalFile", originalKtFile) + } recordOriginalDeclaration( targetDeclaration = dependencyNonLocalDeclaration, @@ -183,7 +193,10 @@ object LowLevelFirApiFacadeForResolveOnAir { fileAnnotation = annotationEntry, replacement = replacement ) - val llFirResolvableSession = firFile.llFirResolvableSession ?: error("FirFile session expected to be a resolvable session") + val llFirResolvableSession = firFile.llFirResolvableSession + ?: errorWithAttachment("FirFile session expected to be a resolvable session but was ${firFile.llFirSession::class.java}") { + withAttachment("firSession", firFile.llFirSession) + } val declarationResolver = llFirResolvableSession.moduleComponents.lazyFirDeclarationsResolver declarationResolver.resolveFileAnnotations( diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirFirClassByPsiClassProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirFirClassByPsiClassProvider.kt index 88af009b63e..447da96b9ef 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirFirClassByPsiClassProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/providers/LLFirFirClassByPsiClassProvider.kt @@ -9,6 +9,7 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiTypeParameter import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withPsiAttachment import org.jetbrains.kotlin.asJava.KtLightClassMarker import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSessionComponent @@ -18,6 +19,7 @@ import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.errorWithAttachment class LLFirFirClassByPsiClassProvider(private val session: LLFirSession) : FirSessionComponent { fun getFirClass(psiClass: PsiClass): FirRegularClassSymbol? { @@ -42,8 +44,11 @@ class LLFirFirClassByPsiClassProvider(private val session: LLFirSession) : FirSe val classId = psiClass.classIdIfNonLocal ?: error("No classId for non-local class") val provider = session.nullableJavaSymbolProvider ?: session.symbolProvider - val symbol = (provider.getClassLikeSymbolByClassId(classId) - ?: error("No classifier found with $classId")) + val symbol = provider.getClassLikeSymbolByClassId(classId) + ?: errorWithAttachment("No classifier found") { + withPsiAttachment("psiClass", psiClass) + withAttachment("classId", classId.asString()) + } return symbol as FirRegularClassSymbol } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt index a7cabeea1d9..4ffe9ee93bb 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/state/LLFirResolvableResolveSession.kt @@ -11,11 +11,14 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.FirTowerContextProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.project.structure.firModuleData -import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.* import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirLibrarySession +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableModuleSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSession +import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirDeclarationForCompiledElementSearcher import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration import org.jetbrains.kotlin.analysis.low.level.api.fir.util.originalDeclaration +import org.jetbrains.kotlin.analysis.low.level.api.fir.util.withPsiAttachment import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.analysis.project.structure.getKtModule import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance @@ -32,6 +35,8 @@ import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.errorWithAttachment +import org.jetbrains.kotlin.utils.withAttachmentDetailed internal abstract class LLFirResolvableResolveSession( private val sessionProvider: LLFirSessionProvider, @@ -98,7 +103,10 @@ internal abstract class LLFirResolvableResolveSession( "Declaration should be resolvable module, instead it had ${module::class}" } val nonLocalNamedDeclaration = ktDeclaration.getNonLocalContainingOrThisDeclaration() - ?: error("Declaration should have non-local container${ktDeclaration.getElementTextInContext()}") + ?: errorWithAttachment("Declaration should have non-local container") { + withPsiAttachment("ktDeclaration", ktDeclaration) + withAttachmentDetailed("module", module) { it.moduleDescription } + } if (ktDeclaration == nonLocalNamedDeclaration) { val session = sessionProvider.getResolvableSession(module) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt index 710f7fb51eb..2d4cb194f73 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/FirDeclarationForCompiledElementSearcher.kt @@ -30,17 +30,20 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid is KtNamedFunction -> findNonLocalFunction(ktDeclaration) is KtProperty -> findNonLocalProperty(ktDeclaration) - else -> error("Unsupported compiled declaration of type ${ktDeclaration::class}: ${ktDeclaration.getElementTextInContext()}") + else -> firErrorWithAttachment("Unsupported compiled declaration of type", psi = ktDeclaration) } } private fun findNonLocalEnumEntry(declaration: KtEnumEntry): FirEnumEntry { require(!declaration.isLocal) val classId = declaration.containingClassOrObject?.getClassId() - ?: error("Non-local class should have classId. The class is ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment( + "Non-local class should have classId. The class is ${declaration.getElementTextInContext()}", + psi = declaration + ) val classCandidate = symbolProvider.getClassLikeSymbolByClassId(classId) - ?: error("We should be able to find a symbol for $classId") + ?: firErrorWithAttachment("We should be able to find a symbol for $classId", psi = declaration) return (classCandidate.fir as? FirRegularClass)?.declarations?.first { it is FirEnumEntry && it.name == declaration.nameAsName @@ -49,26 +52,31 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid private fun findNonLocalClassLikeDeclaration(declaration: KtClassLikeDeclaration): FirClassLikeDeclaration { val classId = declaration.getClassId() - ?: error("Non-local class should have classId. The class is ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("Non-local class should have classId", psi = declaration) val classCandidate = symbolProvider.getClassLikeSymbolByClassId(classId) - ?: error("We should be able to find a symbol for $classId") + ?: firErrorWithAttachment("We should be able to find a symbol for $classId", psi = declaration) { + withAttachment( + "classId", + classId.asString() + ) + } return classCandidate.fir } private fun findConstructorOfNonLocalClass(declaration: KtConstructor<*>): FirConstructor { val containingClass = declaration.containingClassOrObject - ?: error("Constructor must have outer class: ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("Constructor must have outer class", psi = declaration) require(!containingClass.isLocal) val classId = containingClass.getClassId() - ?: error("Non-local class should have classId. The class is ${containingClass.getElementTextInContext()}") + ?: firErrorWithAttachment("Non-local class should have classId", psi = declaration) val constructorCandidate = symbolProvider.getClassDeclaredConstructors(classId) .singleOrNull { representSameConstructor(declaration, it.fir) } - ?: error("We should be able to find a constructor: ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("We should be able to find a constructor", psi = declaration) return constructorCandidate.fir } @@ -79,7 +87,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid val functionCandidate = symbolProvider.findFunctionCandidates(declaration) .singleOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) } - ?: error("We should be able to find a symbol for function ${declaration.name}: ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("We should be able to find a symbol for function", psi = declaration) return functionCandidate.fir } @@ -90,7 +98,7 @@ internal class FirDeclarationForCompiledElementSearcher(private val symbolProvid val propertyCandidate = symbolProvider.findPropertyCandidates(declaration) .singleOrNull { KtDeclarationAndFirDeclarationEqualityChecker.representsTheSameDeclaration(declaration, it.fir) } - ?: error("We should be able to find a symbol for property ${declaration.name}: ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("We should be able to find a symbol for property", psi = declaration) return propertyCandidate.fir } @@ -112,7 +120,7 @@ private fun FirSymbolProvider.findCallableCandidates( } val containerClassId = declaration.containingClassOrObject?.getClassId() - ?: error("No containing non-local declaration found for ${declaration.getElementTextInContext()}") + ?: firErrorWithAttachment("No containing non-local declaration found for", psi = declaration) return getClassDeclaredFunctionSymbols(containerClassId, declaration.nameAsSafeName) + getClassDeclaredPropertySymbols(containerClassId, declaration.nameAsSafeName) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/contrainingFileUtils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/contrainingFileUtils.kt index c2a892a480e..77edf29ce30 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/contrainingFileUtils.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/contrainingFileUtils.kt @@ -17,6 +17,6 @@ fun FirDeclaration.getContainingFile(): FirFile? { is FirFile -> this is FirCallableDeclaration -> provider.getFirCallableContainerFile(symbol) is FirClassLikeDeclaration -> provider.getFirClassifierContainerFile(symbol) - else -> error("Unsupported declaration ${this::class.java}") + else -> firErrorWithAttachment("Unsupported declaration ${this::class.java}", fir = this) } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/declarationUtils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/declarationUtils.kt index bd68fba65ed..6c49b9c0dc6 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/declarationUtils.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/declarationUtils.kt @@ -30,7 +30,7 @@ internal fun KtDeclaration.findSourceNonLocalFirDeclaration( //TODO test what way faster findSourceNonLocalFirDeclarationByProvider(firFileBuilder, provider, containerFirFile)?.let { return it } findSourceOfNonLocalFirDeclarationByTraversingWholeTree(firFileBuilder, containerFirFile)?.let { return it } - error("No fir element was found for\n${getElementTextInContext()}") + firErrorWithAttachment("No fir element was found for", psi = this) } internal fun KtDeclaration.findFirDeclarationForAnyFirSourceDeclaration( @@ -45,7 +45,7 @@ internal fun KtDeclaration.findFirDeclarationForAnyFirSourceDeclaration( firDeclaration.psi == this || firDeclaration.psi == originalDeclaration } return fir - ?: error("FirDeclaration was not found for\n${getElementTextInContext()}") + ?: firErrorWithAttachment("FirDeclaration was not found", psi = this) } internal inline fun KtDeclaration.findFirDeclarationForAnyFirSourceDeclarationOfType( @@ -95,12 +95,12 @@ private fun KtDeclaration.findSourceNonLocalFirDeclarationByProvider( } this is KtConstructor<*> -> { val containingClass = containingClassOrObject - ?: error("Container class should be not null for KtConstructor") + ?: firErrorWithAttachment("Container class should be not null for KtConstructor", psi = this) val containerClassFir = containingClass.findFir(provider) as? FirRegularClass ?: return null containerClassFir.declarations.firstOrNull { it.psi === this } } this is KtTypeAlias -> findFir(provider) - else -> error("Invalid container ${this::class}\n${getElementTextInContext()}") + else -> firErrorWithAttachment("Invalid container", psi = this) } return candidate?.takeIf { it.realPsi == this } } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/exceptionUtils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/exceptionUtils.kt new file mode 100644 index 00000000000..6769941f003 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/exceptionUtils.kt @@ -0,0 +1,80 @@ +/* + * 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.low.level.api.fir.util + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.renderer.ConeTypeRendererForDebugging +import org.jetbrains.kotlin.fir.renderer.FirDeclarationRendererWithAttributes +import org.jetbrains.kotlin.fir.renderer.FirRenderer +import org.jetbrains.kotlin.fir.renderer.FirResolvePhaseRenderer +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments +import org.jetbrains.kotlin.utils.errorWithAttachment +import org.jetbrains.kotlin.utils.withAttachmentDetailed + + +fun KotlinExceptionWithAttachments.withFirAttachment(name: String, fir: FirElement): KotlinExceptionWithAttachments { + withAttachmentDetailed(name, fir, debugFirRenderer::renderElementAsString) + withPsiAttachment("${name}Psi", fir.psi) + return this +} + +fun KotlinExceptionWithAttachments.withFirSymbolAttachment(name: String, symbol: FirBasedSymbol<*>): KotlinExceptionWithAttachments { + withAttachment("${name}Class", symbol::class.java.name) + withFirAttachment("${name}Fir", symbol.fir) + return this +} + +fun KotlinExceptionWithAttachments.withConeTypeAttachment(name: String, coneType: ConeKotlinType): KotlinExceptionWithAttachments { + withAttachmentDetailed(name, coneType) { + buildString { ConeTypeRendererForDebugging(this).render(it) } + } + return this +} + +fun KotlinExceptionWithAttachments.withPsiAttachment(name: String, psi: PsiElement?): KotlinExceptionWithAttachments { + withAttachmentDetailed(name, psi) { psiElement -> + when (psiElement) { + is KtElement -> psiElement.getElementTextInContext() + else -> psiElement.text + } + } + return this +} + + +fun firErrorWithAttachment( + message: String, + cause: Throwable? = null, + fir: FirElement? = null, + coneType: ConeKotlinType? = null, + psi: PsiElement? = null, + attachments: KotlinExceptionWithAttachments.() -> Unit = {} +): Nothing { + errorWithAttachment(message, cause) { + if (fir != null) { + withFirAttachment("fir", fir) + } + + if (psi != null) { + withPsiAttachment("psi", psi) + } + + if (coneType != null) { + withConeTypeAttachment("coneType", coneType) + } + attachments () + } +} + + +private val debugFirRenderer = + FirRenderer(resolvePhaseRenderer = FirResolvePhaseRenderer(), declarationRenderer = FirDeclarationRendererWithAttributes()) diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/utils.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/utils.kt index e9467de8c06..913ebfc06ae 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/utils.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/utils.kt @@ -54,17 +54,14 @@ internal val FirElement.isErrorElement internal val FirDeclaration.ktDeclaration: KtDeclaration get() { val psi = psi - ?: error("PSI element was not found for${render()}") + ?: firErrorWithAttachment("PSI element was not found", fir = this) return when (psi) { is KtDeclaration -> psi is KtObjectLiteralExpression -> psi.objectDeclaration - else -> error( - """ - FirDeclaration.psi (${this::class.simpleName}) should be KtDeclaration but was ${psi::class.simpleName} - ${(psi as? KtElement)?.getElementTextInContext() ?: psi.text} - - ${render()} - """.trimIndent() + else -> firErrorWithAttachment( + "FirDeclaration.psi (${this::class.simpleName}) should be KtDeclaration but was ${psi::class.simpleName}", + fir = this, + psi = psi, ) } } diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinExceptionWithAttachments.kt b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinExceptionWithAttachments.kt index 0ad3b36d97f..f6d9a4f0022 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/KotlinExceptionWithAttachments.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/KotlinExceptionWithAttachments.kt @@ -29,9 +29,19 @@ open class KotlinExceptionWithAttachments : RuntimeException, ExceptionWithAttac } } +fun KotlinExceptionWithAttachments.withAttachmentDetailed( + name: String, + content: T?, + render: (T) -> String +): KotlinExceptionWithAttachments { + withAttachment("${name}Class", content?.let { it::class.java.name }) + withAttachment(name, content?.let(render)) + return this +} + @OptIn(ExperimentalContracts::class) inline fun checkWithAttachment(value: Boolean, lazyMessage: () -> String, attachments: (KotlinExceptionWithAttachments) -> Unit = {}) { - contract { returns() implies(value) } + contract { returns() implies (value) } if (!value) { val e = KotlinExceptionWithAttachments(lazyMessage()) @@ -39,3 +49,14 @@ inline fun checkWithAttachment(value: Boolean, lazyMessage: () -> String, attach throw e } } + +inline fun errorWithAttachment( + message: String, + cause: Throwable? = null, + attachments: KotlinExceptionWithAttachments.() -> Unit = {} +): Nothing { + val exception = KotlinExceptionWithAttachments(message, cause) + attachments(exception) + throw exception +} +