[Analysis API] add some basic tests for symbol substitution

This commit is contained in:
Ilya Kirillov
2022-06-08 17:16:47 +02:00
parent 7b1765cc8b
commit c95ac9f845
42 changed files with 1005 additions and 1 deletions
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.analysis.test.framework.TestWithDisposable
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkersSourceFilePreprocessor
import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.analysis.test.framework.utils.SkipTestException
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
@@ -103,6 +104,7 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
useDirectives(JvmEnvironmentConfigurationDirectives)
useDirectives(SubstitutionParser.Directives)
useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor)
useAdditionalService { ExpressionMarkerProvider() }
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 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.test.framework.services
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.components.buildSubstitutor
import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeParameter
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
object SubstitutionParser {
fun KtAnalysisSession.parseSubstitutors(module: TestModule, file: KtFile): List<KtSubstitutor> {
val directives = module.directives[Directives.SUBSTITUTOR]
if (directives.isEmpty()) return emptyList()
val allTypeParameterSymbols = file
.collectDescendantsOfType<KtTypeParameter>()
.map { it.getTypeParameterSymbol() }
.groupBy { it.name.asString() }
.mapValues { it.value.single() }
return directives.map { subsitutor ->
buildSubstitutor {
subsitutor.forEach { (typeParameterName, typeString) ->
val typeParameterSymbol = allTypeParameterSymbols.getValue(typeParameterName)
val type = with(TypeParser) { parseTypeFromString(typeString, file, allTypeParameterSymbols) }
substitution(typeParameterSymbol, type)
}
}
}
}
object Directives : SimpleDirectivesContainer() {
val SUBSTITUTOR by valueDirective("") { stringValue ->
stringValue.trim().split(";").map { it.trim() }.map { substitution ->
val asList = substitution.split("->").map { it.trim() }
check(asList.size == 2) {
"Substitution should look like `x -> y` but was `$substitution`"
}
asList[0] to asList[1]
}
}
}
}
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2021 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.test.framework.services
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.components.buildClassType
import org.jetbrains.kotlin.analysis.api.components.buildTypeParameterType
import org.jetbrains.kotlin.analysis.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.analysis.api.types.KtType
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
object TypeParser {
fun KtAnalysisSession.parseTypeFromString(
stringType: String,
ktFile: KtFile,
typeParameterByName: Map<String, KtTypeParameterSymbol>
): KtType {
val type = KtPsiFactory(ktFile).createType(stringType)
return convertType(type.typeElement ?: incorrectType(type), typeParameterByName)
}
private fun KtAnalysisSession.convertType(type: KtTypeElement, typeParameterByName: Map<String, KtTypeParameterSymbol>): KtType =
when (type) {
is KtUserType -> {
val qualifier = fullQualifier(type)
if (qualifier in typeParameterByName) {
buildTypeParameterType(typeParameterByName.getValue(qualifier))
} else {
buildClassType(ClassId.topLevel(FqName(qualifier))) {
type.typeArguments.forEach { argument ->
argument(convertType(argument.typeReference?.typeElement ?: incorrectType(type), typeParameterByName))
}
}
}
}
else -> TODO(type::class.java.name)
}
private fun fullQualifier(type: KtUserType) =
type.children.takeWhile { it !is KtTypeArgumentList }.joinToString(separator = ".") { it.text }
private fun incorrectType(type: KtElement): Nothing {
error("Invalid type `${type.text}`")
}
}