[Analysis API] add tests for KtSubstitutor

This commit is contained in:
Ilya Kirillov
2022-06-10 15:58:48 +02:00
parent f4d47cfe59
commit b9907963fe
24 changed files with 347 additions and 31 deletions
@@ -0,0 +1,72 @@
/*
* 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.api.fir.test.cases.generated.cases.types;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.types.AbstractAnalysisApiSubstitutorsTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/substitutors/typeSubstitution")
@TestDataPath("$PROJECT_ROOT")
public class FirIdeDependentAnalysisSourceModuleAnalysisApiSubstitutorsTestGenerated extends AbstractAnalysisApiSubstitutorsTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Dependent,
AnalysisApiMode.Ide
)
);
}
@Test
public void testAllFilesPresentInTypeSubstitution() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/substitutors/typeSubstitution"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("directSubstitution.kt")
public void testDirectSubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/directSubstitution.kt");
}
@Test
@TestMetadata("emptySubstitution.kt")
public void testEmptySubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/emptySubstitution.kt");
}
@Test
@TestMetadata("notApplicableSubstitutor.kt")
public void testNotApplicableSubstitutor() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/notApplicableSubstitutor.kt");
}
@Test
@TestMetadata("substitutionWithTypeParams.kt")
public void testSubstitutionWithTypeParams() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/substitutionWithTypeParams.kt");
}
}
@@ -0,0 +1,72 @@
/*
* 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.api.fir.test.cases.generated.cases.types;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.fir.test.configurators.AnalysisApiFirTestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.types.AbstractAnalysisApiSubstitutorsTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/substitutors/typeSubstitution")
@TestDataPath("$PROJECT_ROOT")
public class FirIdeNormalAnalysisSourceModuleAnalysisApiSubstitutorsTestGenerated extends AbstractAnalysisApiSubstitutorsTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Ide
)
);
}
@Test
public void testAllFilesPresentInTypeSubstitution() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/substitutors/typeSubstitution"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("directSubstitution.kt")
public void testDirectSubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/directSubstitution.kt");
}
@Test
@TestMetadata("emptySubstitution.kt")
public void testEmptySubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/emptySubstitution.kt");
}
@Test
@TestMetadata("notApplicableSubstitutor.kt")
public void testNotApplicableSubstitutor() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/notApplicableSubstitutor.kt");
}
@Test
@TestMetadata("substitutionWithTypeParams.kt")
public void testSubstitutionWithTypeParams() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/substitutionWithTypeParams.kt");
}
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSing
import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
@@ -20,11 +21,11 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractAnalysisApiSignatureSubstitutionTest : AbstractAnalysisApiSingleFileTest() { abstract class AbstractAnalysisApiSignatureSubstitutionTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) { override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtDeclaration>(ktFile) val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtCallableDeclaration>(ktFile)
val actual = analyseForTest(declaration) { val actual = analyseForTest(declaration) {
val symbol = declaration.getSymbolOfType<KtCallableSymbol>() val symbol = declaration.getSymbolOfType<KtCallableSymbol>()
val substitutor = with(SubstitutionParser) { parseSubstitutors(module, ktFile) }.single() val substitutor = SubstitutionParser.parseSubstitutor(ktFile, declaration)
val signatureBeforeSubstitution = symbol.asSignature() val signatureBeforeSubstitution = symbol.asSignature()
val signatureAfterSubstitution = signatureBeforeSubstitution.substitute(substitutor) val signatureAfterSubstitution = signatureBeforeSubstitution.substitute(substitutor)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSing
import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
@@ -20,11 +21,11 @@ import org.jetbrains.kotlin.test.services.assertions
abstract class AbstractAnalysisApiSymbolSubstitutionTest : AbstractAnalysisApiSingleFileTest() { abstract class AbstractAnalysisApiSymbolSubstitutionTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) { override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtDeclaration>(ktFile) val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtCallableDeclaration>(ktFile)
val actual = analyseForTest(declaration) { val actual = analyseForTest(declaration) {
val symbol = declaration.getSymbolOfType<KtCallableSymbol>() val symbol = declaration.getSymbolOfType<KtCallableSymbol>()
val substitutor = with(SubstitutionParser) { parseSubstitutors(module, ktFile) }.single() val substitutor = SubstitutionParser.parseSubstitutor(ktFile, declaration)
val signature = symbol.substitute(substitutor) val signature = symbol.substitute(substitutor)
prettyPrint { prettyPrint {
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components package org.jetbrains.kotlin.analysis.api.impl.base.test.cases.components
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisApiInternals
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.calls.KtCall import org.jetbrains.kotlin.analysis.api.calls.KtCall
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
@@ -20,6 +21,7 @@ import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility import kotlin.reflect.KVisibility
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
@OptIn(KtAnalysisApiInternals::class)
internal fun KtAnalysisSession.stringRepresentation(any: Any): String = with(any) { internal fun KtAnalysisSession.stringRepresentation(any: Any): String = with(any) {
fun KtType.render() = asStringForDebugging().replace('/', '.') fun KtType.render() = asStringForDebugging().replace('/', '.')
fun String.indented() = replace("\n", "\n ") fun String.indented() = replace("\n", "\n ")
@@ -0,0 +1,39 @@
/*
* 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.symbols.KtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.getSymbolOfType
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiSingleFileTest
import org.jetbrains.kotlin.analysis.test.framework.services.SubstitutionParser
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.utils.printer.prettyPrint
import org.jetbrains.kotlin.psi.KtCallableDeclaration
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 AbstractAnalysisApiSubstitutorsTest : AbstractAnalysisApiSingleFileTest() {
override fun doTestByFileStructure(ktFile: KtFile, module: TestModule, testServices: TestServices) {
val declaration = testServices.expressionMarkerProvider.getElementOfTypAtCaret<KtCallableDeclaration>(ktFile)
val actual = analyseForTest(declaration) {
val substitutor = SubstitutionParser.parseSubstitutor(ktFile, declaration)
val symbol = declaration.getSymbolOfType<KtCallableSymbol>()
val type = symbol.returnType
val substituted = substitutor.substitute(type)
val substitutedOrNull = substitutor.substituteOrNull(type)
prettyPrint {
appendLine("PSI type: ${declaration.typeReference?.text}")
appendLine("KtType: ${type.render()}")
appendLine("substitutor.substitute: ${substituted.render()}")
appendLine("substitutor.substituteOrNull: ${substitutedOrNull?.render()}")
}
}
testServices.assertions.assertEqualsToTestDataFileSibling(actual)
}
}
@@ -0,0 +1,72 @@
/*
* 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.api.standalone.fir.test.cases.generated.cases.types;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.analysis.api.standalone.fir.test.AnalysisApiFirStandaloneModeTestConfiguratorFactory;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfiguratorFactoryData;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.TestModuleKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode;
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode;
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.types.AbstractAnalysisApiSubstitutorsTest;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("analysis/analysis-api/testData/substitutors/typeSubstitution")
@TestDataPath("$PROJECT_ROOT")
public class FirStandaloneNormalAnalysisSourceModuleAnalysisApiSubstitutorsTestGenerated extends AbstractAnalysisApiSubstitutorsTest {
@NotNull
@Override
public AnalysisApiTestConfigurator getConfigurator() {
return AnalysisApiFirStandaloneModeTestConfiguratorFactory.INSTANCE.createConfigurator(
new AnalysisApiTestConfiguratorFactoryData(
FrontendKind.Fir,
TestModuleKind.Source,
AnalysisSessionMode.Normal,
AnalysisApiMode.Standalone
)
);
}
@Test
public void testAllFilesPresentInTypeSubstitution() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/analysis-api/testData/substitutors/typeSubstitution"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("directSubstitution.kt")
public void testDirectSubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/directSubstitution.kt");
}
@Test
@TestMetadata("emptySubstitution.kt")
public void testEmptySubstitution() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/emptySubstitution.kt");
}
@Test
@TestMetadata("notApplicableSubstitutor.kt")
public void testNotApplicableSubstitutor() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/notApplicableSubstitutor.kt");
}
@Test
@TestMetadata("substitutionWithTypeParams.kt")
public void testSubstitutionWithTypeParams() throws Exception {
runTest("analysis/analysis-api/testData/substitutors/typeSubstitution/substitutionWithTypeParams.kt");
}
}
@@ -1,3 +1,3 @@
// SUBSTITUTOR: T->kotlin.collections.List<S>;S->kotlin.Long // SUBSTITUTOR: T -> kotlin.collections.List<S>, S -> kotlin.Long
fun <T, S> f<caret>oo(x: List<T>, y: Map<T, List<S>>, k: String): T fun <T, S> f<caret>oo(x: List<T>, y: Map<T, List<S>>, k: String): T
@@ -1,3 +1,3 @@
// SUBSTITUTOR: T->kotlin.collections.List<S>;S->kotlin.Long // SUBSTITUTOR: T-> kotlin.collections.List<S>, S -> kotlin.Long
val <T, S> Map<T, S>.val<caret>ue: List<S> val <T, S> Map<T, S>.val<caret>ue: List<S>
@@ -1,3 +1,3 @@
// SUBSTITUTOR: T->kotlin.collections.List<S>;S->kotlin.Long // SUBSTITUTOR: T -> kotlin.collections.List<S>, S -> kotlin.Long
fun <T, S> f<caret>oo(x: List<T>, y: Map<T, List<S>>, k: String): T fun <T, S> f<caret>oo(x: List<T>, y: Map<T, List<S>>, k: String): T
@@ -1,3 +1,3 @@
// SUBSTITUTOR: T->kotlin.collections.List<S>;S->kotlin.Long // SUBSTITUTOR: T -> kotlin.collections.List<S>, S -> kotlin.Long
val <T, S> Map<T, S>.val<caret>ue: List<S> val <T, S> Map<T, S>.val<caret>ue: List<S>
@@ -0,0 +1,3 @@
// SUBSTITUTOR: A -> kotlin.Int, B -> kotlin.String
fun <A, B> fo<caret>o(): Map<List<A>, B>
@@ -0,0 +1,4 @@
PSI type: Map<List<A>, B>
KtType: kotlin.collections.Map<kotlin.collections.List<A>, B>
substitutor.substitute: kotlin.collections.Map<kotlin.collections.List<kotlin.Int>, kotlin.String>
substitutor.substituteOrNull: kotlin.collections.Map<kotlin.collections.List<kotlin.Int>, kotlin.String>
@@ -0,0 +1,3 @@
// SUBSTITUTOR: A -> kotlin.Int
fun <A> fo<caret>o(): List<String>
@@ -0,0 +1,4 @@
PSI type: List<String>
KtType: kotlin.collections.List<kotlin.String>
substitutor.substitute: kotlin.collections.List<kotlin.String>
substitutor.substituteOrNull: null
@@ -0,0 +1,3 @@
// SUBSTITUTOR:
fun <A> fo<caret>o(): List<String>
@@ -0,0 +1,4 @@
PSI type: List<String>
KtType: kotlin.collections.List<kotlin.String>
substitutor.substitute: kotlin.collections.List<kotlin.String>
substitutor.substituteOrNull: null
@@ -0,0 +1,3 @@
// SUBSTITUTOR: A -> B, B -> kotlin.Int
fun <A, B> fo<caret>o(): Map<List<A>, B>
@@ -0,0 +1,4 @@
PSI type: Map<List<A>, B>
KtType: kotlin.collections.Map<kotlin.collections.List<A>, B>
substitutor.substitute: kotlin.collections.Map<kotlin.collections.List<B>, kotlin.Int>
substitutor.substituteOrNull: kotlin.collections.Map<kotlin.collections.List<B>, kotlin.Int>
@@ -33,3 +33,7 @@ projectTest(jUnitMode = JUnitMode.JUnit5) {
testsJar() testsJar()
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Xcontext-receivers"
}
@@ -104,7 +104,6 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray()) useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
useDirectives(JvmEnvironmentConfigurationDirectives) useDirectives(JvmEnvironmentConfigurationDirectives)
useDirectives(SubstitutionParser.Directives)
useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor) useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor)
useAdditionalService { ExpressionMarkerProvider() } useAdditionalService { ExpressionMarkerProvider() }
@@ -5,45 +5,62 @@
package org.jetbrains.kotlin.analysis.test.framework.services package org.jetbrains.kotlin.analysis.test.framework.services
import com.intellij.psi.PsiComment
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.components.buildSubstitutor import org.jetbrains.kotlin.analysis.api.components.buildSubstitutor
import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor import org.jetbrains.kotlin.analysis.api.types.KtSubstitutor
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameter
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
object SubstitutionParser { object SubstitutionParser {
fun KtAnalysisSession.parseSubstitutors(module: TestModule, file: KtFile): List<KtSubstitutor> { context(KtAnalysisSession)
val directives = module.directives[Directives.SUBSTITUTOR] fun parseSubstitutor(declaration: KtCallableDeclaration): KtSubstitutor {
if (directives.isEmpty()) return emptyList() val comment = declaration.firstChild as PsiComment
val allTypeParameterSymbols = file return parseSubstitutor(comment, declaration)
}
context(KtAnalysisSession)
fun parseSubstitutor(ktFile: KtFile, declaration: KtCallableDeclaration): KtSubstitutor {
val comment = ktFile.children.filterIsInstance<PsiComment>().single { it.text.startsWith(SUBSTITUTOR_PREFIX) }
return parseSubstitutor(comment, declaration)
}
context(KtAnalysisSession)
fun parseSubstitutor(comment: PsiComment, scopeForTypeParameters: KtElement): KtSubstitutor {
val directivesAsString = comment.text.trim()
check(directivesAsString.startsWith(SUBSTITUTOR_PREFIX))
val substitutorAsMap = parseSubstitutions(directivesAsString.removePrefix(SUBSTITUTOR_PREFIX))
val allTypeParameterSymbols = scopeForTypeParameters
.collectDescendantsOfType<KtTypeParameter>() .collectDescendantsOfType<KtTypeParameter>()
.map { it.getTypeParameterSymbol() } .map { it.getTypeParameterSymbol() }
.groupBy { it.name.asString() } .groupBy { it.name.asString() }
.mapValues { it.value.single() } .mapValues { it.value.single() }
return directives.map { subsitutor -> return buildSubstitutor {
buildSubstitutor { substitutorAsMap.forEach { (typeParameterName, typeString) ->
subsitutor.forEach { (typeParameterName, typeString) -> val typeParameterSymbol = allTypeParameterSymbols.getValue(typeParameterName)
val typeParameterSymbol = allTypeParameterSymbols.getValue(typeParameterName) val type = TypeParser.parseTypeFromString(typeString, scopeForTypeParameters, allTypeParameterSymbols)
val type = with(TypeParser) { parseTypeFromString(typeString, file, allTypeParameterSymbols) } substitution(typeParameterSymbol, type)
substitution(typeParameterSymbol, type)
}
} }
} }
} }
object Directives : SimpleDirectivesContainer() { private fun parseSubstitutions(substitutionsAsString: String): List<Pair<String, String>> =
val SUBSTITUTOR by valueDirective("") { stringValue -> substitutionsAsString.trim().split(",")
stringValue.trim().split(";").map { it.trim() }.map { substitution -> .map { it.trim() }
.filter { it.isNotEmpty() }
.map { substitution ->
val asList = substitution.split("->").map { it.trim() } val asList = substitution.split("->").map { it.trim() }
check(asList.size == 2) { check(asList.size == 2) {
"Substitution should look like `x -> y` but was `$substitution`" "Substitution should look like `x -> y` but was `$substitution`"
} }
asList[0] to asList[1] asList[0] to asList[1]
} }
}
} const val SUBSTITUTOR_PREFIX = "// SUBSTITUTOR:"
} }
@@ -15,16 +15,18 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
object TypeParser { object TypeParser {
fun KtAnalysisSession.parseTypeFromString( context (KtAnalysisSession)
fun parseTypeFromString(
stringType: String, stringType: String,
ktFile: KtFile, contextElement: KtElement,
typeParameterByName: Map<String, KtTypeParameterSymbol> typeParameterByName: Map<String, KtTypeParameterSymbol>
): KtType { ): KtType {
val type = KtPsiFactory(ktFile).createType(stringType) val type = KtPsiFactory(contextElement).createType(stringType)
return convertType(type.typeElement ?: incorrectType(type), typeParameterByName) return convertType(type.typeElement ?: incorrectType(type), typeParameterByName)
} }
private fun KtAnalysisSession.convertType(type: KtTypeElement, typeParameterByName: Map<String, KtTypeParameterSymbol>): KtType = context (KtAnalysisSession)
private fun convertType(type: KtTypeElement, typeParameterByName: Map<String, KtTypeParameterSymbol>): KtType =
when (type) { when (type) {
is KtUserType -> { is KtUserType -> {
val qualifier = fullQualifier(type) val qualifier = fullQualifier(type)
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.scopes.AbstractSub
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByFqNameTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByFqNameTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByPsiTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByPsiTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByReferenceTest import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.symbols.AbstractSymbolByReferenceTest
import org.jetbrains.kotlin.analysis.api.impl.base.test.cases.types.AbstractAnalysisApiSubstitutorsTest
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiMode
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisSessionMode
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind
@@ -133,6 +134,12 @@ private fun AnalysisApiTestGroup.generateAnalysisApiNonComponentsTests() {
model("annotationsOnFiles") model("annotationsOnFiles")
} }
} }
group("substitutors", filter = frontendIs(FrontendKind.Fir)) {
test(AbstractAnalysisApiSubstitutorsTest::class) {
model("typeSubstitution")
}
}
} }