Safe reporting PSI elements in KEWA

This commit is contained in:
Vladimir Dolzhenko
2021-09-28 22:04:06 +02:00
committed by teamcity
parent 20871dd555
commit 5dc2442872
16 changed files with 63 additions and 52 deletions
@@ -57,12 +57,12 @@ object DecompiledLightClassesFactory {
innerClass != null,
{ "Could not find corresponding inner/nested class " + relativeFqName + " in class " + decompiledClassOrObject.fqName + "\nFile: " + decompiledClassOrObject.containingKtFile.virtualFile.name },
{
it.withAttachment("decompiledClassOrObject.txt", decompiledClassOrObject.text)
it.withPsiAttachment("decompiledClassOrObject.txt", decompiledClassOrObject)
it.withAttachment("fileClass.txt", decompiledClassOrObject.containingFile::class)
it.withAttachment("file.txt", decompiledClassOrObject.containingFile.text)
it.withAttachment("root.txt", rootLightClassForDecompiledFile.text)
it.withPsiAttachment("file.txt", decompiledClassOrObject.containingFile)
it.withPsiAttachment("root.txt", rootLightClassForDecompiledFile)
it.withAttachment("currentName.txt", current.name)
it.withAttachment("current.txt", current.text)
it.withPsiAttachment("current.txt", current)
it.withAttachment("innerClasses.txt", current.innerClasses.map { psiClass -> psiClass.name })
it.withAttachment("innerName.txt", name.asString())
},
@@ -156,7 +156,7 @@ object LightClassUtil {
checkWithAttachment(parent.parent is KtClassOrObject, {
"Bad parent: ${parent.parent?.javaClass}"
}) {
it.withAttachment("parent", parent.text)
it.withPsiAttachment("parent", parent)
}
return (parent.parent as KtClassOrObject).toLightClass()
}
@@ -21,9 +21,7 @@ public class CompilationException extends KotlinExceptionWithAttachments {
cause);
this.element = element;
if (element != null) {
withAttachment("element.kt", element.getText());
}
withPsiAttachment("element.kt", element);
}
@Nullable
@@ -174,7 +174,7 @@ public class PackageCodegenImpl implements PackageCodegen {
if (fragment == null) {
LOG.error(new KotlinExceptionWithAttachments(
"package fragment is not found for module:" + state.getModule() + " file:" + file)
.withAttachment("file.kt", file.getText()));
.withPsiAttachment("file.kt", file));
} else if (!expectedPackageFqName.equals(fragment.getFqName())) {
LOG.error("expected package fq name: " + expectedPackageFqName + ", actual: " + fragment.getFqName());
}
@@ -123,7 +123,7 @@ public class CodegenBinding {
Type result = asmTypeForAnonymousClassOrNull(bindingContext, expression);
if (result == null) {
throw new KotlinExceptionWithAttachments("Couldn't compute ASM type for expression")
.withAttachment("expression.kt", PsiUtilsKt.getElementTextWithContext(expression));
.withPsiAttachment("expression.kt", expression);
}
return result;
@@ -42,7 +42,7 @@ class MainFunctionDetector {
this.getFunctionDescriptor = { function ->
bindingContext.get(BindingContext.FUNCTION, function)
?: throw throw KotlinExceptionWithAttachments("No descriptor resolved for $function")
.withAttachment("function.text", function.text)
.withPsiAttachment("function.text", function)
}
this.languageVersionSettings = languageVersionSettings
}
@@ -146,13 +146,13 @@ fun getEnclosingDescriptor(context: BindingContext, element: KtElement): Declara
val declaration =
element.getParentOfTypeCodeFragmentAware(KtNamedDeclaration::class.java)
?: throw KotlinExceptionWithAttachments("No parent KtNamedDeclaration for of type ${element.javaClass}")
.withAttachment("element.kt", element.text)
.withPsiAttachment("element.kt", element)
return if (declaration is KtFunctionLiteral) {
getEnclosingDescriptor(context, declaration)
} else {
context.get(DECLARATION_TO_DESCRIPTOR, declaration)
?: throw KotlinExceptionWithAttachments("No descriptor for named declaration of type ${declaration.javaClass}")
.withAttachment("declaration.kt", declaration.text)
.withPsiAttachment("declaration.kt", declaration)
}
}
@@ -626,8 +626,8 @@ class PSICallResolver(
}
if (allValueArguments.isEmpty()) {
throw KotlinExceptionWithAttachments("Can not find an external argument for 'set' method")
.withAttachment("callElement.kt", oldCall.callElement.text)
.withAttachment("file.kt", oldCall.callElement.takeIf { it.isValid }?.containingFile?.text ?: "<no file>")
.withPsiAttachment("callElement.kt", oldCall.callElement)
.withPsiAttachment("file.kt", oldCall.callElement.takeIf { it.isValid }?.containingFile)
}
allValueArguments.last()
} else {
@@ -24,6 +24,6 @@ class NoDescriptorForDeclarationException @JvmOverloads constructor(declaration:
+ (additionalDetails?.let { "\n---------------------------------------------------\n$it" } ?: "")
) {
init {
withAttachment("declaration.kt", declaration.getElementTextWithContext())
withPsiAttachment("declaration.kt", declaration)
}
}
@@ -252,7 +252,7 @@ public abstract class ExpressionTypingVisitorDispatcher extends KtVisitor<Kotlin
// This trows AssertionError in CLI and reports the error in the IDE
LOG.error(
new KotlinExceptionWithAttachments("Exception while analyzing expression" + location, e)
.withAttachment("expression.kt", expression.getText())
.withPsiAttachment("expression.kt", expression)
);
}
catch (AssertionError errorFromLogger) {
@@ -15,13 +15,6 @@ class KotlinFrontEndException(message: String, cause: Throwable) : KotlinExcepti
cause: Throwable,
element: PsiElement
) : this(getExceptionMessage("Front-end", message, cause, PsiDiagnosticUtils.atLocation(element)), cause) {
withAttachment(
"element.kt",
if (element.isValid) {
element.text
} else {
"PsiElement (invalid): " + element.toString()
}
)
withPsiAttachment("element.kt", element)
}
}
@@ -72,7 +72,7 @@ class KtDotQualifiedExpression : KtExpressionImplStub<KotlinPlaceHolderStub<KtDo
LOG.error(
KotlinExceptionWithAttachments(
"Invalid stub structure. DOT_QUALIFIED_EXPRESSION must have one or two children. Was: ${expressions.size}\n"
).withAttachment("file.kt", containingFile.text)
).withPsiAttachment("file.kt", containingFile)
)
return null
}
@@ -316,7 +316,7 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
checkWithAttachment(declarations.size == 1, { "unexpected ${declarations.size} declarations" }) {
it.withAttachment("text.kt", text)
for (d in declarations.withIndex()) {
it.withAttachment("declaration${d.index}.kt", d.value.text)
it.withPsiAttachment("declaration${d.index}.kt", d.value)
}
}
@Suppress("UNCHECKED_CAST")
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.psi.psiUtil
import com.intellij.injected.editor.VirtualFileWindow
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
@@ -381,29 +380,7 @@ fun PsiElement.startsWithComment(): Boolean = firstChild is PsiComment
// ---------------------------------- Debug/logging ----------------------------------------------------------------------------------------
fun PsiElement.getElementTextWithContext(): String {
if (!isValid) return "<invalid element $this>"
if (this is PsiFile) {
return containingFile.text
}
// Find parent for element among file children
val topLevelElement = PsiTreeUtil.findFirstParent(this, { it.parent is PsiFile })
?: throw AssertionError("For non-file element we should always be able to find parent in file children")
val startContextOffset = topLevelElement.startOffset
val elementContextOffset = textRange.startOffset
val inFileParentOffset = elementContextOffset - startContextOffset
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()
}
fun PsiElement.getElementTextWithContext(): String = org.jetbrains.kotlin.utils.getElementTextWithContext(this)
fun PsiElement.getTextWithLocation(): String = "'${this.text}' at ${PsiDiagnosticUtils.atLocation(this)}"
@@ -5,9 +5,11 @@
package org.jetbrains.kotlin.utils
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.openapi.diagnostic.ExceptionWithAttachments
import java.nio.charset.StandardCharsets
import com.intellij.psi.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -38,6 +40,12 @@ open class KotlinExceptionWithAttachments : RuntimeException, ExceptionWithAttac
attachments.add(Attachment(name, content?.toString() ?: "<null>"))
return this
}
fun withPsiAttachment(name: String, element: PsiElement?): KotlinExceptionWithAttachments {
kotlin.runCatching { ApplicationManager.getApplication().runReadAction<String> { element?.let(::getElementTextWithContext) } }
.getOrNull()?.let { withAttachment(name, it) }
return this
}
}
@@ -50,4 +58,4 @@ inline fun checkWithAttachment(value: Boolean, lazyMessage: () -> String, attach
attachments(e)
throw e
}
}
}
@@ -0,0 +1,35 @@
/*
* 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.utils
import com.intellij.injected.editor.VirtualFileWindow
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
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
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()
}