Add additional options to DescriptorRender

1) renderContextNearLocalVariable - if true renderer add fq name for local variables after "@" symbol
2) fullContextForLocalVariable - if true then renderer add all fq names for local variables, else only last one
3) useBaseClassAsReceiver - if true then renderer uses dispatch receiver as extension receiver if the last is null
This commit is contained in:
Ivan Cilcic
2019-08-11 17:28:08 +03:00
committed by Mikhail Glukhikh
parent 1879fd19eb
commit 6fa7435cd4
4 changed files with 108 additions and 20 deletions
@@ -5,21 +5,22 @@
package org.jetbrains.kotlin.compiler.visualizer
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.compiler.visualizer.Annotator.annotate
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.types.*
class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): BaseRenderer {
class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : BaseRenderer {
val bindingContext = analysisResult.bindingContext
private val annotations = mutableListOf<Annotator.AnnotationInfo>()
@@ -31,6 +32,14 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
withSourceFileForTopLevel = false
renderConstructorKeyword = false
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
includeAdditionalModifiers = false
renderContextNearLocalVariable = true
fullContextForLocalVariable = false
useBaseClassAsReceiver = true
}
private fun addAnnotation(text: String, range: TextRange) {
annotations.add(Annotator.AnnotationInfo(text, range))
}
override fun render(): String {
@@ -39,6 +48,18 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
}
inner class Renderer : KtVisitorVoid() {
private fun renderType(type: KotlinType?): String {
return type?.let { descriptorRenderer.renderType(it) } ?: "[ERROR: unknown type]"
}
private fun renderType(descriptor: CallableDescriptor?): String {
return renderType(descriptor?.returnType)
}
private fun renderType(expression: KtExpression?): String {
return renderType(expression?.let { bindingContext.getType(it) })
}
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
@@ -47,27 +68,67 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
element.acceptChildren(this)
}
override fun visitProperty(property: KtProperty) {
val descriptor = bindingContext[VARIABLE, property]
annotations.add(Annotator.AnnotationInfo(descriptor?.returnType.toString(), property.nameIdentifier!!.textRange))
super.visitProperty(property)
private fun renderVariableType(variable: KtVariableDeclaration) {
val descriptor = bindingContext[VARIABLE, variable]
addAnnotation(renderType(descriptor), variable.nameIdentifier!!.textRange)
variable.acceptChildren(this)
}
override fun visitProperty(property: KtProperty) =
renderVariableType(property)
override fun visitDestructuringDeclarationEntry(multiDeclarationEntry: KtDestructuringDeclarationEntry) =
renderVariableType(multiDeclarationEntry)
override fun visitTypeReference(typeReference: KtTypeReference) {
val type = bindingContext[TYPE, typeReference]
addAnnotation(renderType(type), typeReference.textRange)
}
override fun visitConstantExpression(expression: KtConstantExpression) {
val type = bindingContext.getType(expression)
annotations.add(Annotator.AnnotationInfo(type.toString(), expression.textRange))
addAnnotation(renderType(expression), expression.textRange)
}
private fun renderCall(call: Call): String {
val resolvedCall = bindingContext[RESOLVED_CALL, call] ?: return "[ERROR: not resolved]"
val descriptor = resolvedCall.resultingDescriptor
var annotation = descriptorRenderer.render(descriptor)
if (descriptor.name.asString() == "<init>") annotation = annotation.replace("(", "<init>(")
resolvedCall.typeArguments.forEach { (type, value) ->
annotation = annotation.replaceFirst(type.name.toString(), "$value")
}
return annotation
}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val resolvedCall = bindingContext[RESOLVED_CALL, bindingContext[CALL, expression]]
if (resolvedCall == null || resolvedCall.resultingDescriptor == null) {
val call = expression.getCall(bindingContext)
if (call != null) {
addAnnotation(renderCall(call), expression.textRange)
super.visitSimpleNameExpression(expression)
return
} else {
val qualifierDescriptor = bindingContext[QUALIFIER, expression]?.descriptor
if (qualifierDescriptor != null) {
addAnnotation(descriptorRenderer.render(qualifierDescriptor), expression.textRange)
}
}
}
val annotation = descriptorRenderer.render(resolvedCall.resultingDescriptor)
val fqName = DescriptorUtils.getFqName(resolvedCall.resultingDescriptor.containingDeclaration)
annotations.add(Annotator.AnnotationInfo("$fqName.$annotation", expression.textRange))
override fun visitIfExpression(expression: KtIfExpression) {
addAnnotation(renderType(expression), expression.ifKeyword.textRange)
super.visitIfExpression(expression)
}
override fun visitWhenExpression(expression: KtWhenExpression) {
addAnnotation(renderType(expression), expression.whenKeyword.textRange)
super.visitWhenExpression(expression)
}
override fun visitWhenEntry(jetWhenEntry: KtWhenEntry) {
val arrowTextRange = jetWhenEntry.expression?.textRange?.shiftLeft(3)
?: jetWhenEntry.conditions.last().textRange.shiftRight(3)
addAnnotation(renderType(jetWhenEntry.expression), arrowTextRange)
super.visitWhenEntry(jetWhenEntry)
}
}
}