[Analysis API] add tests for checking KtType internals

This commit is contained in:
Ilya Kirillov
2022-12-16 13:55:05 +01:00
committed by teamcity
parent 2b94a33937
commit 61d8b91c89
3 changed files with 126 additions and 13 deletions
@@ -0,0 +1,22 @@
/*
* 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.analysis.api.impl.base.test.cases.types
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
abstract class AbstractTypeByDeclarationReturnTypeTest : AbstractTypeTest() {
context(KtAnalysisSession)
override fun getType(ktFile: KtFile, module: TestModule, testServices: TestServices): KtType {
val declarationAtCaret = testServices.expressionMarkerProvider.getElementOfTypeAtCaret<KtDeclaration>(ktFile)
return declarationAtCaret.getReturnKtType()
}
}
@@ -0,0 +1,28 @@
/*
* 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.analysis.api.impl.base.test.cases.types
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.symbols.DebugSymbolRenderer
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractTypeTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val actual = analyseForTest(ktFile.declarations.first()) {
val type = getType(ktFile, module, testServices)
DebugSymbolRenderer(renderTypeByProperties = true).renderType(type)
}
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
}
context(KtAnalysisSession)
protected abstract fun getType(ktFile: KtFile, module: TestModule, testServices: TestServices): KtType
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.api.contracts.description.renderKtContractE
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.analysis.api.symbols.markers.KtPossiblyNamedSymbol
import org.jetbrains.kotlin.analysis.api.types.KtClassErrorType
import org.jetbrains.kotlin.analysis.api.types.KtClassTypeQualifier
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.analysis.project.structure.KtModule
import org.jetbrains.kotlin.analysis.utils.printer.PrettyPrinter
@@ -28,14 +29,18 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.types.Variance
import java.lang.reflect.InvocationTargetException
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KProperty
import kotlin.reflect.KVisibility
import kotlin.reflect.full.*
import kotlin.reflect.jvm.isAccessible
public class DebugSymbolRenderer(
public val renderExtra: Boolean = false,
public val renderTypeByProperties: Boolean = false,
) {
context(KtAnalysisSession)
@@ -64,13 +69,17 @@ public class DebugSymbolRenderer(
.declaredMemberExtensionFunctions
.filter { it.name == "getContainingModule" }
.forEach {
appendLine()
renderFunction(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol)
}
KtSymbolInfoProviderMixIn::class.declaredMemberExtensionProperties
.asSequence()
.filter { (it.extensionReceiverParameter?.type?.classifier as? KClass<*>)?.isInstance(symbol) == true }
.forEach { renderProperty(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol) }
.forEach {
appendLine()
renderProperty(it, renderSymbolsFully = false, this@KtAnalysisSession, symbol)
}
}
}
@@ -80,29 +89,33 @@ public class DebugSymbolRenderer(
renderSymbolHeader(symbol)
withIndent {
appendLine()
renderProperty(KtCallableSymbol::callableIdIfNonLocal, renderSymbolsFully = false, symbol)
if (symbol is KtNamedSymbol) {
appendLine()
renderProperty(KtNamedSymbol::name, renderSymbolsFully = false, symbol)
}
appendLine()
renderProperty(KtCallableSymbol::origin, renderSymbolsFully = false, symbol)
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderFunction(function: KFunction<*>, renderSymbolsFully: Boolean, vararg args: Any) {
appendLine().append(function.name).append(": ")
append(function.name).append(": ")
renderFunctionCall(function, renderSymbolsFully, args)
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderProperty(property: KProperty<*>, renderSymbolsFully: Boolean, vararg args: Any) {
appendLine().append(property.name).append(": ")
append(property.name).append(": ")
renderFunctionCall(property.getter, renderSymbolsFully, args)
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderFunctionCall(function: KFunction<*>, renderSymbolsFully: Boolean, args: Array<out Any>) {
try {
function.isAccessible = true
renderValue(function.call(*args), renderSymbolsFully)
} catch (e: InvocationTargetException) {
append("Could not render due to ").appendLine(e.cause.toString())
@@ -114,18 +127,18 @@ public class DebugSymbolRenderer(
renderSymbolHeader(symbol)
val apiClass = getSymbolApiClass(symbol)
withIndent {
apiClass.members
.asSequence()
val members = apiClass.members
.filterIsInstance<KProperty<*>>()
.filter { it.name !in ignoredPropertyNames }
.sortedBy { it.name }
.forEach { member ->
renderProperty(
member,
renderSymbolsFully = member.name == KtValueParameterSymbol::generatedPrimaryConstructorProperty.name,
symbol
)
}
appendLine()
printCollectionIfNotEmpty(members, separator = "\n") { member ->
renderProperty(
member,
renderSymbolsFully = member.name == KtValueParameterSymbol::generatedPrimaryConstructorProperty.name,
symbol
)
}
}
}
@@ -187,6 +200,10 @@ public class DebugSymbolRenderer(
context(KtAnalysisSession)
private fun PrettyPrinter.renderType(type: KtType) {
if (renderTypeByProperties) {
renderByPropertyNames(type)
return
}
if (type.annotations.isNotEmpty()) {
renderList(type.annotations, renderSymbolsFully = false)
append(' ')
@@ -197,6 +214,22 @@ public class DebugSymbolRenderer(
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderByPropertyNames(value: Any) {
val members = value::class.members
.filter { it.name !in ignoredPropertyNames }
.filter { it.visibility != KVisibility.PRIVATE && it.visibility != KVisibility.INTERNAL }
.sortedBy { it.name }
.filterIsInstance<KProperty<*>>()
printCollectionIfNotEmpty(members, separator = "\n") { member ->
renderProperty(
member,
renderSymbolsFully = false,
value
)
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderAnnotationApplication(call: KtAnnotationApplication) {
renderValue(call.classId, renderSymbolsFully = false)
@@ -229,6 +262,8 @@ public class DebugSymbolRenderer(
// Symbol-related values
is KtSymbol -> renderSymbolTag(value, renderSymbolsFully)
is KtType -> renderType(value)
is KtTypeProjection -> renderTypeProjection(value)
is KtClassTypeQualifier -> renderTypeQualifier(value)
is KtAnnotationValue -> renderAnnotationValue(value)
is KtContractEffectDeclaration -> Context(this@KtAnalysisSession, this@renderValue, this@DebugSymbolRenderer)
.renderKtContractEffectDeclaration(value, endWithNewLine = false)
@@ -256,6 +291,27 @@ public class DebugSymbolRenderer(
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderTypeProjection(value: KtTypeProjection) {
when (value) {
is KtStarTypeProjection -> append("*")
is KtTypeArgumentWithVariance -> {
if (value.variance != Variance.INVARIANT) {
append("${value.variance.label} ")
}
renderType(value.type)
}
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.renderTypeQualifier(value: KtClassTypeQualifier) {
appendLine("qualifier:")
withIndent {
renderByPropertyNames(value)
}
}
context(KtAnalysisSession)
private fun PrettyPrinter.rendeContextReceiver(receiver: KtContextReceiver) {
append("ContextReceiver(")
@@ -335,7 +391,14 @@ public class DebugSymbolRenderer(
else lines.first() + " ..."
}
private val ignoredPropertyNames = setOf("psi", "token")
private val ignoredPropertyNames = setOf(
"psi",
"token",
"builder",
"coneType",
"analysisContext",
"fe10Type"
)
private val symbolImplementationPackageNames = listOf(
"org.jetbrains.kotlin.analysis.api.fir",