diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt index 361a46adc50..4f044675450 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirCompletionCandidateChecker.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirOfType import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.ResolutionParameters import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.SingleCandidateResolutionMode import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.SingleCandidateResolver -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirVariable @@ -90,7 +90,7 @@ internal class KtFirCompletionCandidateChecker( ): Sequence?> { val towerDataContext = analysisSession.firResolveSession.getTowerContextProvider(originalFile) .getClosestAvailableParentContext(fakeNameExpression) - ?: error("Cannot find enclosing declaration for ${fakeNameExpression.getElementTextInContext()}") + ?: error("Cannot find enclosing declaration for ${fakeNameExpression.getElementTextWithContext()}") return sequence { yield(null) // otherwise explicit receiver won't be checked when there are no implicit receivers in completion position diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt index 6236e4dd5ae..e51a6780a2d 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirScopeProvider.kt @@ -25,7 +25,7 @@ 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.api.getOrBuildFirFile import org.jetbrains.kotlin.analysis.utils.errors.unexpectedElementError -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirResolvePhase @@ -194,7 +194,7 @@ internal class KtFirScopeProvider( ): KtScopeContext { val towerDataContext = analysisSession.firResolveSession.getTowerContextProvider(originalFile).getClosestAvailableParentContext(positionInFakeFile) - ?: error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextInContext()}") + ?: error("Cannot find enclosing declaration for ${positionInFakeFile.getElementTextWithContext()}") val towerDataElementsIndexed = towerDataContext.towerDataElements.asReversed().withIndex() val implicitReceivers = towerDataElementsIndexed.flatMap { (index, towerDataElement) -> diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt index 57c7a03a70c..66b4b3436af 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/symbols/firSymbolUtils.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.KtContextReceiverImpl import org.jetbrains.kotlin.analysis.api.symbols.KtValueParameterSymbol import org.jetbrains.kotlin.analysis.api.types.KtType import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression @@ -39,7 +39,7 @@ internal fun FirCallableSymbol<*>.invalidModalityError(): Nothing { | |$rendered | - |${(fir.psi as? KtDeclaration)?.getElementTextInContext()}""".trimMargin() + |${(fir.psi as? KtDeclaration)?.getElementTextWithContext()}""".trimMargin() ) } diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/exceptionWithAttachmentBuilderHelpers.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/exceptionWithAttachmentBuilderHelpers.kt index f11259e9524..84f713da811 100644 --- a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/exceptionWithAttachmentBuilderHelpers.kt +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/errors/exceptionWithAttachmentBuilderHelpers.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.analysis.utils.errors import com.intellij.psi.PsiElement import com.intellij.psi.PsiInvalidElementAccessException import org.jetbrains.kotlin.analysis.project.structure.KtModule -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.utils.exceptions.ExceptionAttachmentBuilder @@ -20,7 +20,7 @@ public fun ExceptionAttachmentBuilder.withPsiEntry(name: String, psi: PsiElement withEntry(name, psi) { psiElement -> when { !psiElement.isValid -> "INVALID PSI ${PsiInvalidElementAccessException.findOutInvalidationReason(psiElement)}" - psiElement is KtElement -> psiElement.getElementTextInContext() + psiElement is KtElement -> psiElement.getElementTextWithContext() else -> psiElement.text } } diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/psiUtils.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/psiUtils.kt index d93bedb8b58..ece38e47665 100644 --- a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/psiUtils.kt +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/printer/psiUtils.kt @@ -6,11 +6,7 @@ package org.jetbrains.kotlin.analysis.utils.printer import com.intellij.psi.PsiElement -import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -26,25 +22,3 @@ public inline fun PsiElement.parentsOfType(withSelf: Bo parentsOfType(T::class.java, withSelf) -public fun KtElement.getElementTextInContext(): String { - @Suppress("LocalVariableName") val ELEMENT_TAG = "ELEMENT" - val context = parentOfType() - ?: parentOfType() - ?: PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java) - ?: getNonStrictParentOfType() - ?: containingKtFile - val builder = StringBuilder() - context.accept(object : PsiElementVisitor() { - override fun visitElement(element: PsiElement) { - if (element === this@getElementTextInContext) builder.append("<$ELEMENT_TAG>") - if (element is LeafPsiElement) { - builder.append(element.text) - } else { - element.acceptChildren(this) - } - if (element === this@getElementTextInContext) builder.append("") - } - }) - return builder.toString().trimIndent().trim() -} - diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/DuplicatedFirSourceElementsException.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/DuplicatedFirSourceElementsException.kt index 1d8b2e70384..f587c3c3f4b 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/DuplicatedFirSourceElementsException.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/DuplicatedFirSourceElementsException.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.psi.KtElement @@ -25,7 +25,7 @@ class DuplicatedFirSourceElementsException( | ${newFir.render().trim()} | |PSI element is $psi with text in context: - |${psi.getElementTextInContext()}""".trimMargin() + |${psi.getElementTextWithContext()}""".trimMargin() companion object { diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt index a78928bbfdd..ea47a28d96b 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/element/builder/FirElementBuilder.kt @@ -12,10 +12,9 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.LLFirModuleResolveCompone import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FileStructureElement import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FirElementsRecorder -import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.KtToFirMapping import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.declarationCanBeLazilyResolved import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.analysis.utils.printer.parentOfType import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType import org.jetbrains.kotlin.fir.FirAnnotationContainer @@ -34,7 +33,6 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase -import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -53,7 +51,7 @@ internal class FirElementBuilder( KtQualifiedExpression with KtCallExpression in selector transformed in FIR to FirFunctionCall expression Which will have a receiver as qualifier */ - deparenthesized.selectorExpression ?: error("Incomplete code:\n${element.getElementTextInContext()}") + deparenthesized.selectorExpression ?: error("Incomplete code:\n${element.getElementTextWithContext()}") } deparenthesized is KtValueArgument -> { // null will be return in case of invalid KtValueArgument 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 d3e1ada6dda..165c878c424 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 @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analysis.project.structure.KtModule import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance import org.jetbrains.kotlin.analysis.utils.errors.withPsiEntry -import org.jetbrains.kotlin.analysis.utils.printer.getElementTextInContext +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.FirDeclaration @@ -114,7 +114,7 @@ internal abstract class LLFirResolvableResolveSession( private fun findFirCompiledSymbol(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> { require(ktDeclaration.containingKtFile.isCompiled) { - "This method will only work on compiled declarations, but this declaration is not compiled: ${ktDeclaration.getElementTextInContext()}" + "This method will only work on compiled declarations, but this declaration is not compiled: ${ktDeclaration.getElementTextWithContext()}" } val session = getSessionFor(module) diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/psiUtils.kt b/compiler/util/src/org/jetbrains/kotlin/utils/psiUtils.kt index 7bf3e21ab2a..f9db127927f 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/psiUtils.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/psiUtils.kt @@ -5,31 +5,51 @@ package org.jetbrains.kotlin.utils -import com.intellij.injected.editor.VirtualFileWindow -import com.intellij.psi.* -import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiFile +import com.intellij.psi.impl.source.tree.LeafPsiElement fun getElementTextWithContext(psiElement: PsiElement): String { if (!psiElement.isValid) return "" - if (psiElement is PsiFile) { - return psiElement.containingFile.text - } - - // Find parent for element among file children - val topLevelElement = PsiTreeUtil.findFirstParent(psiElement) { it.parent is PsiFile } - ?: throw AssertionError("For non-file element we should always be able to find parent in file children") - - val startContextOffset = topLevelElement.textRange.startOffset - val elementContextOffset = psiElement.textRange.startOffset - - val inFileParentOffset = elementContextOffset - startContextOffset - - + @Suppress("LocalVariableName") val ELEMENT_TAG = "ELEMENT" val containingFile = psiElement.containingFile - val isInjected = containingFile is VirtualFileWindow - return StringBuilder(topLevelElement.text) - .insert(inFileParentOffset, "") - .insert(0, "File name: ${containingFile.name} Physical: ${containingFile.isPhysical} Injected: $isInjected\n") - .toString() + val context = psiElement.parentOfType("KtImportDirective") + ?: psiElement.parentOfType("KtPackageDirective") + ?: psiElement.parentOfType("KtDeclarationWithBody", "KtClassOrObject", "KtScript") + ?: psiElement.parentOfType("KtProperty") + ?: containingFile + val elementTextInContext = buildString { + context.accept(object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + if (element === psiElement) append("<$ELEMENT_TAG>") + if (element is LeafPsiElement) { + append(element.text) + } else { + element.acceptChildren(this) + } + if (element === psiElement) append("") + } + }) + }.trimIndent().trim() + + return buildString { + appendLine("") + append(elementTextInContext) + } +} + +private fun PsiElement.parentOfType(vararg psiClassNames: String): PsiElement? { + fun acceptsClass(javaClass: Class<*>): Boolean { + if (javaClass.simpleName in psiClassNames) return true + javaClass.superclass?.let { if (acceptsClass(it)) return true } + for (superInterface in javaClass.interfaces) { + if (acceptsClass(superInterface)) return true + } + return false + } + return generateSequence(this) { it.parent } + .filter { it !is PsiFile } + .firstOrNull { acceptsClass(it::class.java) } } \ No newline at end of file