[Tests] Move stub info extraction for test into a separate utility

KTIJ-28668
This commit is contained in:
Pavel Kirpichenkov
2024-02-15 17:08:38 +02:00
committed by Space Team
parent f6a35ef0e5
commit 604dec7a87
2 changed files with 151 additions and 142 deletions
@@ -5,158 +5,17 @@
package org.jetbrains.kotlin.analysis.decompiler.stub.files
import com.intellij.psi.stubs.StubElement
import com.intellij.util.indexing.FileContentImpl
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinClsStubBuilder
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.psi.KtProjectionKind
import org.jetbrains.kotlin.psi.stubs.impl.*
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.nio.file.Paths
import kotlin.reflect.KClass
abstract class AbstractAdditionalStubInfoTest : AbstractDecompiledClassTest() {
fun runTest(testDirectory: String) {
val testDirectoryPath = Paths.get(testDirectory)
val testData = TestData.createFromDirectory(testDirectoryPath)
val stub = KotlinClsStubBuilder().buildFileStub(FileContentImpl.createByFile(getClassFileToDecompile(testData, false)))!!
val builder = StringBuilder()
extractAdditionInfo(stub, builder, 0)
KotlinTestUtils.assertEqualsToFile(testData.getExpectedFile(useK2ToCompileCode), builder.toString())
KotlinTestUtils.assertEqualsToFile(testData.getExpectedFile(useK2ToCompileCode), extractAdditionalStubInfo(stub))
testData.checkIfIdentical(useK2ToCompileCode)
}
private fun extractAdditionInfo(stub: StubElement<*>, builder: StringBuilder, level: Int) {
builder.append(stub.toString())
when (stub) {
is KotlinUserTypeStubImpl -> {
val upperBound = stub.upperBound
if (upperBound != null) {
builder.append(" ft: ")
appendFlexibleTypeInfo(builder, upperBound)
}
}
is KotlinFunctionStubImpl -> {
val contract = stub.contract
if (contract != null) {
for (element in contract) {
builder.append("\n" + " ".repeat(level)).append("effect:")
element.accept(KotlinContractRenderer(builder), null)
}
}
}
is KotlinPropertyStubImpl -> {
val initializer = stub.constantInitializer
if (initializer != null) {
builder.append("\n").append(" ".repeat(level)).append("initializer: ${initializer.value}")
}
}
is KotlinAnnotationEntryStubImpl -> {
val arguments = stub.valueArguments
if (arguments != null) {
builder
.append("\n")
.append(" ".repeat(level))
.append("valueArguments: ")
.append(arguments.entries.joinToString(", ", "(", ")") { "${it.key.asString()} = ${it.value}" })
}
}
is KotlinParameterStubImpl -> {
stub.functionTypeParameterName?.let { builder.append(" paramNameByAnnotation: ").append(it) }
}
}
for (child in stub.childrenStubs) {
builder.append("\n").append(" ".repeat(level))
extractAdditionInfo(child, builder, level + 1)
}
}
private fun appendFlexibleTypeInfo(builder: StringBuilder, typeBean: KotlinTypeBean) {
when (typeBean) {
is KotlinClassTypeBean -> {
builder.append(typeBean.classId.asFqNameString())
val arguments = typeBean.arguments
if (arguments.isNotEmpty()) {
builder.append("<")
arguments.forEachIndexed { index, arg ->
if (index > 0) builder.append(", ")
if (arg.projectionKind != KtProjectionKind.NONE) {
builder.append(arg.projectionKind.name)
}
if (arg.projectionKind != KtProjectionKind.STAR) {
appendFlexibleTypeInfo(builder, arg.type!!)
}
}
builder.append(">")
}
if (typeBean.nullable) {
builder.append("?")
}
}
is KotlinTypeParameterTypeBean -> {
builder.append(typeBean.typeParameterName)
if (typeBean.nullable) {
builder.append("?")
}
if (typeBean.definitelyNotNull) {
builder.append(" & Any")
}
}
is KotlinFlexibleTypeBean -> {
appendFlexibleTypeInfo(builder, typeBean.lowerBound)
builder.append(" .. ")
appendFlexibleTypeInfo(builder, typeBean.upperBound)
}
}
}
}
class KotlinContractRenderer(private val buffer: StringBuilder) : KtContractDescriptionVisitor<Unit, Nothing?, KotlinTypeBean, Nothing?>() {
override fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
conditionalEffect.effect.accept(this, data)
buffer.append(" -> ")
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("Returns(")
returnsEffect.value.accept(this, data)
buffer.append(")")
}
override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("CallsInPlace(")
callsEffect.valueParameterReference.accept(this, data)
buffer.append(", ${callsEffect.kind})")
}
override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression<KotlinTypeBean, Nothing?>, data: Nothing?) {
binaryLogicExpression.left.accept(this, data)
buffer.append(" ${binaryLogicExpression.kind.token} ")
binaryLogicExpression.right.accept(this, data)
}
override fun visitLogicalNot(logicalNot: KtLogicalNot<KotlinTypeBean, Nothing?>, data: Nothing?) {
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
isInstancePredicate.arg.accept(this, data)
buffer.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}")
}
override fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
isNullPredicate.arg.accept(this, data)
buffer.append(" ${if (isNullPredicate.isNegated) "!=" else "=="} null")
}
override fun visitConstantDescriptor(constantReference: KtConstantReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append(constantReference.name)
}
override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("param(").append(valueParameterReference.parameterIndex).append(")")
}
}
@@ -0,0 +1,150 @@
/*
* Copyright 2010-2024 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.analysis.decompiler.stub.files
import com.intellij.psi.stubs.StubElement
import org.jetbrains.kotlin.contracts.description.*
import org.jetbrains.kotlin.psi.KtProjectionKind
import org.jetbrains.kotlin.psi.stubs.impl.*
fun extractAdditionalStubInfo(stub: StubElement<*>): String {
val builder = StringBuilder()
extractAdditionInfo(stub, builder, 0)
return builder.toString()
}
private fun extractAdditionInfo(stub: StubElement<*>, builder: StringBuilder, level: Int) {
builder.append(stub.toString())
when (stub) {
is KotlinUserTypeStubImpl -> {
val upperBound = stub.upperBound
if (upperBound != null) {
builder.append(" ft: ")
appendFlexibleTypeInfo(builder, upperBound)
}
}
is KotlinFunctionStubImpl -> {
val contract = stub.contract
if (contract != null) {
for (element in contract) {
builder.append("\n" + " ".repeat(level)).append("effect:")
element.accept(KotlinContractRenderer(builder), null)
}
}
}
is KotlinPropertyStubImpl -> {
val initializer = stub.constantInitializer
if (initializer != null) {
builder.append("\n").append(" ".repeat(level)).append("initializer: ${initializer.value}")
}
}
is KotlinAnnotationEntryStubImpl -> {
val arguments = stub.valueArguments
if (arguments != null) {
builder
.append("\n")
.append(" ".repeat(level))
.append("valueArguments: ")
.append(arguments.entries.joinToString(", ", "(", ")") { "${it.key.asString()} = ${it.value}" })
}
}
is KotlinParameterStubImpl -> {
stub.functionTypeParameterName?.let { builder.append(" paramNameByAnnotation: ").append(it) }
}
}
for (child in stub.childrenStubs) {
builder.append("\n").append(" ".repeat(level))
extractAdditionInfo(child, builder, level + 1)
}
}
private fun appendFlexibleTypeInfo(builder: StringBuilder, typeBean: KotlinTypeBean) {
when (typeBean) {
is KotlinClassTypeBean -> {
builder.append(typeBean.classId.asFqNameString())
val arguments = typeBean.arguments
if (arguments.isNotEmpty()) {
builder.append("<")
arguments.forEachIndexed { index, arg ->
if (index > 0) builder.append(", ")
if (arg.projectionKind != KtProjectionKind.NONE) {
builder.append(arg.projectionKind.name)
}
if (arg.projectionKind != KtProjectionKind.STAR) {
appendFlexibleTypeInfo(builder, arg.type!!)
}
}
builder.append(">")
}
if (typeBean.nullable) {
builder.append("?")
}
}
is KotlinTypeParameterTypeBean -> {
builder.append(typeBean.typeParameterName)
if (typeBean.nullable) {
builder.append("?")
}
if (typeBean.definitelyNotNull) {
builder.append(" & Any")
}
}
is KotlinFlexibleTypeBean -> {
appendFlexibleTypeInfo(builder, typeBean.lowerBound)
builder.append(" .. ")
appendFlexibleTypeInfo(builder, typeBean.upperBound)
}
}
}
class KotlinContractRenderer(private val buffer: StringBuilder) : KtContractDescriptionVisitor<Unit, Nothing?, KotlinTypeBean, Nothing?>() {
override fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
conditionalEffect.effect.accept(this, data)
buffer.append(" -> ")
conditionalEffect.condition.accept(this, data)
}
override fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("Returns(")
returnsEffect.value.accept(this, data)
buffer.append(")")
}
override fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("CallsInPlace(")
callsEffect.valueParameterReference.accept(this, data)
buffer.append(", ${callsEffect.kind})")
}
override fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression<KotlinTypeBean, Nothing?>, data: Nothing?) {
binaryLogicExpression.left.accept(this, data)
buffer.append(" ${binaryLogicExpression.kind.token} ")
binaryLogicExpression.right.accept(this, data)
}
override fun visitLogicalNot(logicalNot: KtLogicalNot<KotlinTypeBean, Nothing?>, data: Nothing?) {
logicalNot.arg.accept(this, data)
}
override fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
isInstancePredicate.arg.accept(this, data)
buffer.append(" ${if (isInstancePredicate.isNegated) "!" else ""}is ${isInstancePredicate.type}")
}
override fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate<KotlinTypeBean, Nothing?>, data: Nothing?) {
isNullPredicate.arg.accept(this, data)
buffer.append(" ${if (isNullPredicate.isNegated) "!=" else "=="} null")
}
override fun visitConstantDescriptor(constantReference: KtConstantReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append(constantReference.name)
}
override fun visitValueParameterReference(valueParameterReference: KtValueParameterReference<KotlinTypeBean, Nothing?>, data: Nothing?) {
buffer.append("param(").append(valueParameterReference.parameterIndex).append(")")
}
}