Move PsiElement.getElementTextWithContext to psi module with small adjustments

* replace existing function (the new one makes it more clear which element was printed)
* prints position for element
* handles invalid psi element
This commit is contained in:
Ilya Kirillov
2023-06-30 17:15:56 +02:00
committed by Space Team
parent 4b3bff3344
commit c5014d4765
9 changed files with 56 additions and 64 deletions
@@ -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.ResolutionParameters
import org.jetbrains.kotlin.analysis.low.level.api.fir.resolver.SingleCandidateResolutionMode 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.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.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirVariable import org.jetbrains.kotlin.fir.declarations.FirVariable
@@ -90,7 +90,7 @@ internal class KtFirCompletionCandidateChecker(
): Sequence<ImplicitReceiverValue<*>?> { ): Sequence<ImplicitReceiverValue<*>?> {
val towerDataContext = analysisSession.firResolveSession.getTowerContextProvider(originalFile) val towerDataContext = analysisSession.firResolveSession.getTowerContextProvider(originalFile)
.getClosestAvailableParentContext(fakeNameExpression) .getClosestAvailableParentContext(fakeNameExpression)
?: error("Cannot find enclosing declaration for ${fakeNameExpression.getElementTextInContext()}") ?: error("Cannot find enclosing declaration for ${fakeNameExpression.getElementTextWithContext()}")
return sequence { return sequence {
yield(null) // otherwise explicit receiver won't be checked when there are no implicit receivers in completion position yield(null) // otherwise explicit receiver won't be checked when there are no implicit receivers in completion position
@@ -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.LLFirResolveSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFirFile 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.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.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
@@ -194,7 +194,7 @@ internal class KtFirScopeProvider(
): KtScopeContext { ): KtScopeContext {
val towerDataContext = val towerDataContext =
analysisSession.firResolveSession.getTowerContextProvider(originalFile).getClosestAvailableParentContext(positionInFakeFile) 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 towerDataElementsIndexed = towerDataContext.towerDataElements.asReversed().withIndex()
val implicitReceivers = towerDataElementsIndexed.flatMap { (index, towerDataElement) -> val implicitReceivers = towerDataElementsIndexed.flatMap { (index, towerDataElement) ->
@@ -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.symbols.KtValueParameterSymbol
import org.jetbrains.kotlin.analysis.api.types.KtType 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.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.descriptors.ClassKind
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
@@ -39,7 +39,7 @@ internal fun FirCallableSymbol<*>.invalidModalityError(): Nothing {
| |
|$rendered |$rendered
| |
|${(fir.psi as? KtDeclaration)?.getElementTextInContext()}""".trimMargin() |${(fir.psi as? KtDeclaration)?.getElementTextWithContext()}""".trimMargin()
) )
} }
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.analysis.utils.errors
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiInvalidElementAccessException import com.intellij.psi.PsiInvalidElementAccessException
import org.jetbrains.kotlin.analysis.project.structure.KtModule 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.psi.KtElement
import org.jetbrains.kotlin.utils.exceptions.ExceptionAttachmentBuilder import org.jetbrains.kotlin.utils.exceptions.ExceptionAttachmentBuilder
@@ -20,7 +20,7 @@ public fun ExceptionAttachmentBuilder.withPsiEntry(name: String, psi: PsiElement
withEntry(name, psi) { psiElement -> withEntry(name, psi) { psiElement ->
when { when {
!psiElement.isValid -> "INVALID PSI ${PsiInvalidElementAccessException.findOutInvalidationReason(psiElement)}" !psiElement.isValid -> "INVALID PSI ${PsiInvalidElementAccessException.findOutInvalidationReason(psiElement)}"
psiElement is KtElement -> psiElement.getElementTextInContext() psiElement is KtElement -> psiElement.getElementTextWithContext()
else -> psiElement.text else -> psiElement.text
} }
} }
@@ -6,11 +6,7 @@
package org.jetbrains.kotlin.analysis.utils.printer package org.jetbrains.kotlin.analysis.utils.printer
import com.intellij.psi.PsiElement 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 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.parents
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -26,25 +22,3 @@ public inline fun <reified T : PsiElement> PsiElement.parentsOfType(withSelf: Bo
parentsOfType(T::class.java, withSelf) parentsOfType(T::class.java, withSelf)
public fun KtElement.getElementTextInContext(): String {
@Suppress("LocalVariableName") val ELEMENT_TAG = "ELEMENT"
val context = parentOfType<KtImportDirective>()
?: parentOfType<KtPackageDirective>()
?: PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java)
?: getNonStrictParentOfType<KtProperty>()
?: 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("</$ELEMENT_TAG>")
}
})
return builder.toString().trimIndent().trim()
}
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder 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.FirElement
import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
@@ -25,7 +25,7 @@ class DuplicatedFirSourceElementsException(
| ${newFir.render().trim()} | ${newFir.render().trim()}
| |
|PSI element is $psi with text in context: |PSI element is $psi with text in context:
|${psi.getElementTextInContext()}""".trimMargin() |${psi.getElementTextWithContext()}""".trimMargin()
companion object { companion object {
@@ -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.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.FileStructureElement
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.structure.FirElementsRecorder 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.lazy.resolve.declarationCanBeLazilyResolved
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.findSourceNonLocalFirDeclaration 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.parentOfType
import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType import org.jetbrains.kotlin.analysis.utils.printer.parentsOfType
import org.jetbrains.kotlin.fir.FirAnnotationContainer 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.psi
import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf 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 KtQualifiedExpression with KtCallExpression in selector transformed in FIR to FirFunctionCall expression
Which will have a receiver as qualifier 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 -> { deparenthesized is KtValueArgument -> {
// null will be return in case of invalid KtValueArgument // null will be return in case of invalid KtValueArgument
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.analysis.utils.errors.withPsiEntry 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.FirElement
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -114,7 +114,7 @@ internal abstract class LLFirResolvableResolveSession(
private fun findFirCompiledSymbol(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> { private fun findFirCompiledSymbol(ktDeclaration: KtDeclaration, module: KtModule): FirBasedSymbol<*> {
require(ktDeclaration.containingKtFile.isCompiled) { 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) val session = getSessionFor(module)
@@ -5,31 +5,51 @@
package org.jetbrains.kotlin.utils package org.jetbrains.kotlin.utils
import com.intellij.injected.editor.VirtualFileWindow import com.intellij.psi.PsiElement
import com.intellij.psi.* import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
fun getElementTextWithContext(psiElement: PsiElement): String { fun getElementTextWithContext(psiElement: PsiElement): String {
if (!psiElement.isValid) return "<invalid element $psiElement>" if (!psiElement.isValid) return "<invalid element $psiElement>"
if (psiElement is PsiFile) { @Suppress("LocalVariableName") val ELEMENT_TAG = "ELEMENT"
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
val containingFile = psiElement.containingFile val containingFile = psiElement.containingFile
val isInjected = containingFile is VirtualFileWindow val context = psiElement.parentOfType("KtImportDirective")
return StringBuilder(topLevelElement.text) ?: psiElement.parentOfType("KtPackageDirective")
.insert(inFileParentOffset, "<caret>") ?: psiElement.parentOfType("KtDeclarationWithBody", "KtClassOrObject", "KtScript")
.insert(0, "File name: ${containingFile.name} Physical: ${containingFile.isPhysical} Injected: $isInjected\n") ?: psiElement.parentOfType("KtProperty")
.toString() ?: 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("</$ELEMENT_TAG>")
}
})
}.trimIndent().trim()
return buildString {
appendLine("<File name: ${containingFile.name}, Physical: ${containingFile.isPhysical}>")
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) }
} }