Wrote separate descriptor renderer
It was easier to write my own renderer than change current one
This commit is contained in:
committed by
Mikhail Glukhikh
parent
b57c34fd4d
commit
3a12bac910
+359
-51
@@ -5,47 +5,45 @@
|
||||
|
||||
package org.jetbrains.kotlin.compiler.visualizer
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.compiler.visualizer.Annotator.annotate
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
|
||||
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import java.util.ArrayList
|
||||
|
||||
class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : BaseRenderer {
|
||||
val bindingContext = analysisResult.bindingContext
|
||||
private val annotations = mutableListOf<Annotator.AnnotationInfo>()
|
||||
|
||||
val descriptorRenderer: DescriptorRenderer = DescriptorRenderer.withOptions {
|
||||
withDefinedIn = false
|
||||
modifiers = emptySet()
|
||||
receiverAfterName = false
|
||||
startFromName = false
|
||||
withSourceFileForTopLevel = false
|
||||
renderConstructorKeyword = false
|
||||
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
|
||||
includeAdditionalModifiers = false
|
||||
renderContextNearLocalVariable = true
|
||||
fullContextForLocalVariable = false
|
||||
useBaseClassAsReceiver = true
|
||||
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
|
||||
}
|
||||
val descriptorRenderer = PsiDescriptorRenderer()
|
||||
|
||||
private val unnecessaryData = mapOf(
|
||||
"kotlin." to "",
|
||||
"collections." to ""
|
||||
"kotlin/" to ""
|
||||
)
|
||||
|
||||
private fun addAnnotation(annotationText: String, element: PsiElement) {
|
||||
annotations.removeIf { it.range.startOffset == element.textRange.startOffset }
|
||||
private fun addAnnotation(annotationText: String, element: PsiElement, deleteDuplicate: Boolean = true) {
|
||||
annotations.removeIf { it.range.startOffset == element.textRange.startOffset && deleteDuplicate }
|
||||
|
||||
var textWithOutUnnecessaryData = annotationText
|
||||
for ((key, value) in unnecessaryData) {
|
||||
@@ -62,6 +60,9 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba
|
||||
}
|
||||
|
||||
inner class Renderer : KtVisitorVoid() {
|
||||
private val implicitReceivers = mutableListOf<ReceiverValue>()
|
||||
private var lastCallWithLambda = ""
|
||||
|
||||
private fun renderType(type: KotlinType?): String {
|
||||
return type?.let { descriptorRenderer.renderType(it) } ?: "[ERROR: unknown type]"
|
||||
}
|
||||
@@ -107,8 +108,25 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba
|
||||
renderVariableType(multiDeclarationEntry)
|
||||
|
||||
override fun visitTypeReference(typeReference: KtTypeReference) {
|
||||
val type = typeReference.getAbbreviatedTypeOrType(bindingContext)
|
||||
addAnnotation(renderType(type), typeReference)
|
||||
if (typeReference.text.isNotEmpty()) {
|
||||
val hasResolvedCall = with(object : KtVisitorVoid() {
|
||||
var hasCall: Boolean = false
|
||||
override fun visitKtElement(element: KtElement) {
|
||||
if (!hasCall) {
|
||||
hasCall = element.getResolvedCall(bindingContext) != null
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
typeReference.accept(this)
|
||||
this.hasCall
|
||||
}
|
||||
|
||||
if (!hasResolvedCall) {
|
||||
val type = typeReference.getAbbreviatedTypeOrType(bindingContext)
|
||||
addAnnotation(renderType(type), typeReference)
|
||||
}
|
||||
}
|
||||
super.visitTypeReference(typeReference)
|
||||
}
|
||||
|
||||
@@ -116,47 +134,41 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba
|
||||
addAnnotation(renderType(expression), expression)
|
||||
}
|
||||
|
||||
private fun renderCall(call: Call): String {
|
||||
val resolvedCall = bindingContext[RESOLVED_CALL, call] ?: return "[ERROR: not resolved]"
|
||||
private fun renderCall(expression: KtExpression): ResolvedCall<out CallableDescriptor>? {
|
||||
val call = expression.getCall(bindingContext)
|
||||
val resolvedCall = expression.getResolvedCall(bindingContext)
|
||||
if (call == null) {
|
||||
return null
|
||||
} else if (resolvedCall == null) {
|
||||
addAnnotation("[ERROR: not resolved]", expression)
|
||||
return null
|
||||
}
|
||||
|
||||
val descriptor = resolvedCall.resultingDescriptor
|
||||
var annotation =
|
||||
if (descriptor.isSpecial()) descriptorRenderer.renderSpecialFunction(descriptor)
|
||||
else descriptorRenderer.render(descriptor)
|
||||
if (descriptor is ClassConstructorDescriptor) annotation = annotation.replace("(", "<init>(")
|
||||
val annotation = descriptorRenderer.render(descriptor)
|
||||
addAnnotation(annotation, expression, deleteDuplicate = false)
|
||||
|
||||
resolvedCall.typeArguments.forEach { (type, value) ->
|
||||
val newValue = if (value is ErrorType) "???" else "$value"
|
||||
annotation = annotation.replaceFirst(type.name.toString(), newValue)
|
||||
fun addReceiverAnnotation(receiver: ReceiverValue?, receiverKind: ExplicitReceiverKind) {
|
||||
if (receiver != null && resolvedCall.explicitReceiverKind != receiverKind) {
|
||||
val index = implicitReceivers.indexOf(receiver)
|
||||
if (index != -1) {
|
||||
addAnnotation("this@$index", expression, deleteDuplicate = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotation
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.isSpecial(): Boolean {
|
||||
return this.name.asString().contains("SPECIAL-FUNCTION")
|
||||
}
|
||||
addReceiverAnnotation(resolvedCall.extensionReceiver, ExplicitReceiverKind.EXTENSION_RECEIVER)
|
||||
addReceiverAnnotation(resolvedCall.dispatchReceiver, ExplicitReceiverKind.DISPATCH_RECEIVER)
|
||||
|
||||
private fun DescriptorRenderer.renderSpecialFunction(descriptor: CallableDescriptor): String {
|
||||
val descriptorName = descriptor.name.asString()
|
||||
val name = when {
|
||||
descriptorName.contains("ELVIS") -> "?:"
|
||||
descriptorName.contains("EXCLEXCL") -> "!!"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
return "fun $name " + this.renderValueParameters(descriptor.valueParameters, false) +
|
||||
": " + renderType(descriptor)
|
||||
return resolvedCall
|
||||
}
|
||||
|
||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||
val call = expression.getCall(bindingContext)
|
||||
if (call != null) {
|
||||
addAnnotation(renderCall(call), expression)
|
||||
super.visitSimpleNameExpression(expression)
|
||||
val qualifierDescriptor = bindingContext[QUALIFIER, expression]?.descriptor
|
||||
if (qualifierDescriptor != null) {
|
||||
addAnnotation(descriptorRenderer.render(qualifierDescriptor), expression)
|
||||
} else {
|
||||
val qualifierDescriptor = bindingContext[QUALIFIER, expression]?.descriptor
|
||||
if (qualifierDescriptor != null) {
|
||||
addAnnotation(descriptorRenderer.render(qualifierDescriptor), expression)
|
||||
}
|
||||
renderCall(expression)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,5 +186,301 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : Ba
|
||||
addAnnotation(renderType(jetWhenEntry.expression), jetWhenEntry.expression!!)
|
||||
super.visitWhenEntry(jetWhenEntry)
|
||||
}
|
||||
|
||||
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
|
||||
val descriptor = bindingContext[DECLARATION_TO_DESCRIPTOR, lambdaExpression.functionLiteral] as AnonymousFunctionDescriptor
|
||||
val extensionReceiver = descriptor.extensionReceiverParameter ?: return super.visitLambdaExpression(lambdaExpression)
|
||||
addAnnotation("$lastCallWithLambda@${implicitReceivers.size}", lambdaExpression)
|
||||
|
||||
implicitReceivers += extensionReceiver.value
|
||||
super.visitLambdaExpression(lambdaExpression)
|
||||
implicitReceivers -= extensionReceiver.value
|
||||
}
|
||||
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
val resolvedCall = renderCall(expression) ?: return super.visitCallExpression(expression)
|
||||
|
||||
if (expression.getChildOfType<KtLambdaArgument>() != null) {
|
||||
lastCallWithLambda = resolvedCall.resultingDescriptor.name.asString()
|
||||
}
|
||||
for (child in expression.children) {
|
||||
if (child.node.elementType != KtNodeTypes.REFERENCE_EXPRESSION) {
|
||||
child.accept(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class PsiDescriptorRenderer : DeclarationDescriptorVisitor<Unit, StringBuilder> {
|
||||
private val typeRenderer: DescriptorRenderer = DescriptorRenderer.withOptions {
|
||||
withDefinedIn = false
|
||||
modifiers = emptySet()
|
||||
classifierNamePolicy = /*ClassifierNamePolicy.SOURCE_CODE_QUALIFIED*/object : ClassifierNamePolicy {
|
||||
override fun renderClassifier(classifier: ClassifierDescriptor, renderer: DescriptorRenderer): String {
|
||||
return (if (classifier is TypeParameterDescriptor) renderer.renderName(classifier.name, false)
|
||||
else renderer.renderFqName(DescriptorUtils.getFqName(classifier))).replace(".", "/")
|
||||
}
|
||||
}
|
||||
includeAdditionalModifiers = false
|
||||
parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE
|
||||
|
||||
withoutTypeParameters = true
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.isSpecial(): Boolean {
|
||||
return this.name.asString().contains("SPECIAL-FUNCTION")
|
||||
}
|
||||
|
||||
fun render(declarationDescriptor: DeclarationDescriptor): String {
|
||||
if (declarationDescriptor is CallableDescriptor && declarationDescriptor.isSpecial()) {
|
||||
return this.renderSpecialFunction(declarationDescriptor)
|
||||
}
|
||||
return buildString {
|
||||
declarationDescriptor.accept(this@PsiDescriptorRenderer, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun renderType(type: KotlinType): String {
|
||||
return typeRenderer.renderType(type)
|
||||
}
|
||||
|
||||
private fun renderName(descriptor: DeclarationDescriptor, hasReceiver: Boolean = false): String {
|
||||
return if (hasReceiver) {
|
||||
descriptor.name.asString()
|
||||
} else {
|
||||
renderFqName(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderFqName(descriptor: DeclarationDescriptor): String {
|
||||
/*generateSequence(descriptor) { it.containingDeclaration }
|
||||
.takeWhile { }*/
|
||||
return descriptor.fqNameUnsafe.render().let {
|
||||
if (descriptor !is PropertyDescriptor && descriptor !is VariableDescriptor)
|
||||
it.replace(".", "/")
|
||||
else it
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderReceiver(descriptor: CallableDescriptor, data: StringBuilder): ReceiverParameterDescriptor? {
|
||||
return descriptor.extensionReceiverParameter?.also {
|
||||
visitReceiverParameterDescriptor(it, data)
|
||||
data.append(".")
|
||||
} ?: descriptor.dispatchReceiverParameter?.also {
|
||||
data.append("(")
|
||||
visitReceiverParameterDescriptor(it, data)
|
||||
data.append(").")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderSuperTypes(klass: ClassDescriptor, builder: StringBuilder) {
|
||||
if (KotlinBuiltIns.isNothing(klass.defaultType)) return
|
||||
|
||||
val supertypes = klass.typeConstructor.supertypes
|
||||
if (supertypes.isEmpty() || supertypes.size == 1 && KotlinBuiltIns.isAnyOrNullableAny(supertypes.iterator().next())) return
|
||||
|
||||
builder.append(": ")
|
||||
supertypes.joinTo(builder, ", ") { renderType(it) }
|
||||
}
|
||||
|
||||
private fun renderValueParameter(
|
||||
parameter: ValueParameterDescriptor, includeNames: Boolean, data: StringBuilder, topLevel: Boolean
|
||||
) {
|
||||
renderVariable(parameter, includeNames, data, topLevel)
|
||||
if (parameter.declaresOrInheritsDefaultValue()) {
|
||||
data.append(" = ...")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderVariable(variable: VariableDescriptor, includeName: Boolean, data: StringBuilder, topLevel: Boolean) {
|
||||
val realType = variable.type
|
||||
|
||||
val varargElementType = (variable as? ValueParameterDescriptor)?.varargElementType
|
||||
val typeToRender = varargElementType ?: realType
|
||||
if (varargElementType != null) data.append("vararg ")
|
||||
|
||||
if (topLevel && variable !is ValueParameterDescriptor) data.append(if (variable.isVar) "var" else "val").append(" ")
|
||||
if (includeName) {
|
||||
data.append(renderName(variable)).append(": ")
|
||||
}
|
||||
|
||||
data.append(renderType(typeToRender))
|
||||
}
|
||||
|
||||
private fun renderSpecialFunction(descriptor: CallableDescriptor): String {
|
||||
val descriptorName = descriptor.name.asString()
|
||||
val name = when {
|
||||
descriptorName.contains("ELVIS") -> "?:"
|
||||
descriptorName.contains("EXCLEXCL") -> "!!"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
val valueParameters = buildString { visitValueParameters(descriptor.valueParameters, this) }
|
||||
val returnType = descriptor.returnType?.let { renderType(it) } ?: "[ERROR: unknown type]"
|
||||
|
||||
return "fun $name $valueParameters: $returnType"
|
||||
}
|
||||
|
||||
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: StringBuilder) {
|
||||
data.append("package-fragment ${renderFqName(descriptor)}")
|
||||
}
|
||||
|
||||
override fun visitPackageViewDescriptor(descriptor: PackageViewDescriptor, data: StringBuilder) {
|
||||
data.append("package ${renderFqName(descriptor)}")
|
||||
}
|
||||
|
||||
override fun visitVariableDescriptor(variable: VariableDescriptor, data: StringBuilder) {
|
||||
renderVariable(variable, true, data, true)
|
||||
}
|
||||
|
||||
override fun visitFunctionDescriptor(function: FunctionDescriptor, data: StringBuilder) {
|
||||
data.append("fun ")
|
||||
|
||||
visitTypeParameters(function.typeParameters, data)
|
||||
if (function.typeParameters.isNotEmpty()) data.append(" ")
|
||||
|
||||
//render receiver
|
||||
val receiver = renderReceiver(function, data)
|
||||
|
||||
//render name
|
||||
data.append(renderName(function, receiver != null))
|
||||
|
||||
//render value parameters
|
||||
visitValueParameters(function.valueParameters, data)
|
||||
|
||||
//render return type
|
||||
val returnType = function.returnType
|
||||
data.append(": ").append(if (returnType == null) "[NULL]" else renderType(returnType))
|
||||
|
||||
renderWhereSuffix(function.typeParameters, data)
|
||||
}
|
||||
|
||||
private fun renderWhereSuffix(typeParameters: List<TypeParameterDescriptor>, data: StringBuilder) {
|
||||
val upperBoundStrings = ArrayList<String>(0)
|
||||
|
||||
for (typeParameter in typeParameters) {
|
||||
typeParameter.upperBounds
|
||||
.drop(1) // first parameter is rendered by renderTypeParameter
|
||||
.mapTo(upperBoundStrings) { renderName(typeParameter) + " : " + renderType(it) }
|
||||
}
|
||||
|
||||
if (upperBoundStrings.isNotEmpty()) {
|
||||
data.append(" where ")
|
||||
upperBoundStrings.joinTo(data, ", ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitTypeParameters(typeParameters: List<TypeParameterDescriptor>, data: StringBuilder) {
|
||||
if (typeParameters.isNotEmpty()) {
|
||||
data.append("<")
|
||||
val iterator = typeParameters.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val typeParameterDescriptor = iterator.next()
|
||||
visitTypeParameterDescriptor(typeParameterDescriptor, data)
|
||||
if (iterator.hasNext()) {
|
||||
data.append(", ")
|
||||
}
|
||||
}
|
||||
data.append(">")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeParameterDescriptor(typeParameter: TypeParameterDescriptor, data: StringBuilder) {
|
||||
data.append(renderName(typeParameter, true))
|
||||
val upperBoundsCount = typeParameter.upperBounds.size
|
||||
if (upperBoundsCount >= 1) {
|
||||
val upperBound = typeParameter.upperBounds.iterator().next()
|
||||
if (!KotlinBuiltIns.isDefaultBound(upperBound)) {
|
||||
data.append(" : ").append(renderType(upperBound))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitClassDescriptor(klass: ClassDescriptor, data: StringBuilder) {
|
||||
data.append(DescriptorRenderer.getClassifierKindPrefix(klass)).append(" ")
|
||||
|
||||
//render name
|
||||
data.append(renderName(klass))
|
||||
|
||||
if (klass.kind == ClassKind.ENUM_ENTRY) return
|
||||
|
||||
visitTypeParameters(klass.declaredTypeParameters, data)
|
||||
|
||||
renderSuperTypes(klass, data)
|
||||
renderWhereSuffix(klass.declaredTypeParameters, data)
|
||||
}
|
||||
|
||||
override fun visitTypeAliasDescriptor(typeAlias: TypeAliasDescriptor, data: StringBuilder) {
|
||||
data.append("typealias").append(" ")
|
||||
data.append(renderName(typeAlias))
|
||||
|
||||
visitTypeParameters(typeAlias.declaredTypeParameters, data)
|
||||
|
||||
data.append(" = ").append(renderType(typeAlias.underlyingType))
|
||||
}
|
||||
|
||||
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: StringBuilder) {
|
||||
data.append(renderName(descriptor))
|
||||
}
|
||||
|
||||
override fun visitConstructorDescriptor(constructor: ConstructorDescriptor, data: StringBuilder) {
|
||||
data.append("constructor").append(" ")
|
||||
|
||||
val classDescriptor = constructor.containingDeclaration
|
||||
|
||||
data.append(renderName(classDescriptor))
|
||||
visitTypeParameters(constructor.typeParameters, data)
|
||||
|
||||
visitValueParameters(constructor.valueParameters, data)
|
||||
|
||||
renderWhereSuffix(constructor.typeParameters, data)
|
||||
}
|
||||
|
||||
override fun visitScriptDescriptor(scriptDescriptor: ScriptDescriptor, data: StringBuilder) {
|
||||
visitClassDescriptor(scriptDescriptor, data)
|
||||
}
|
||||
|
||||
override fun visitPropertyDescriptor(property: PropertyDescriptor, data: StringBuilder) {
|
||||
data.append(if (property.isVar) "var" else "val").append(" ")
|
||||
visitTypeParameters(property.typeParameters, data)
|
||||
if (property.typeParameters.isNotEmpty()) data.append(" ")
|
||||
|
||||
//render receiver
|
||||
val receiver = renderReceiver(property, data)
|
||||
|
||||
//render name
|
||||
data.append(renderName(property, receiver != null))
|
||||
|
||||
//render return type
|
||||
data.append(": ").append(renderType(property.type))
|
||||
|
||||
renderWhereSuffix(property.typeParameters, data)
|
||||
}
|
||||
|
||||
private fun visitValueParameters(parameters: List<ValueParameterDescriptor>, data: StringBuilder) {
|
||||
data.append("(")
|
||||
for ((index, parameter) in parameters.withIndex()) {
|
||||
renderValueParameter(parameter, false, data, false)
|
||||
if (index != parameters.size - 1) {
|
||||
data.append(", ")
|
||||
}
|
||||
}
|
||||
data.append(")")
|
||||
}
|
||||
|
||||
override fun visitValueParameterDescriptor(parameter: ValueParameterDescriptor, data: StringBuilder) {
|
||||
renderValueParameter(parameter, true, data, true)
|
||||
}
|
||||
|
||||
override fun visitPropertyGetterDescriptor(descriptor: PropertyGetterDescriptor, data: StringBuilder) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun visitPropertySetterDescriptor(descriptor: PropertySetterDescriptor, data: StringBuilder) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun visitReceiverParameterDescriptor(descriptor: ReceiverParameterDescriptor, data: StringBuilder) {
|
||||
data.append(renderType(descriptor.type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user