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.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<ImplicitReceiverValue<*>?> {
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
@@ -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) ->
@@ -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()
)
}
@@ -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
}
}
@@ -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 <reified T : PsiElement> PsiElement.parentsOfType(withSelf: Bo
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
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 {
@@ -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
@@ -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)
@@ -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 "<invalid element $psiElement>"
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, "<caret>")
.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("</$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) }
}