[Analysis API] add some basic tests for symbol substitution
This commit is contained in:
+2
@@ -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() }
|
||||
|
||||
+49
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -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}`")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user