FIR IDE: add tests for building kt symbols by PSI
This commit is contained in:
@@ -88,6 +88,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolve
|
||||
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
|
||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.AbstractSymbolsByPsiBuildingTest
|
||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
|
||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest
|
||||
import org.jetbrains.kotlin.idea.highlighter.*
|
||||
@@ -935,14 +936,18 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
testGroup("idea/idea-frontend-fir/tests", "idea/idea-frontend-fir/testData") {
|
||||
testClass<AbstractKtDeclarationAndFirDeclarationEqualityChecker> {
|
||||
model("ktDeclarationAndFirDeclarationEqualityChecker")
|
||||
}
|
||||
testClass<AbstractKtDeclarationAndFirDeclarationEqualityChecker> {
|
||||
model("ktDeclarationAndFirDeclarationEqualityChecker")
|
||||
}
|
||||
|
||||
testClass<AbstractResolveCallTest> {
|
||||
model("analysisSession/resolveCall")
|
||||
testClass<AbstractResolveCallTest> {
|
||||
model("analysisSession/resolveCall")
|
||||
}
|
||||
|
||||
testClass<AbstractSymbolsByPsiBuildingTest> {
|
||||
model("symbolsByPsi")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") {
|
||||
testClass<AbstractFirMultiModuleResolveTest> {
|
||||
|
||||
@@ -4,6 +4,8 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":kotlin-reflect-api"))
|
||||
|
||||
compileOnly(project(":compiler:psi"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
compileOnly(project(":core:type-system"))
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.symbols
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.TypeInfo
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.jvm.javaGetter
|
||||
|
||||
object DebugSymbolRenderer {
|
||||
fun render(symbol: KtSymbol): String = buildString {
|
||||
val klass = symbol::class
|
||||
appendLine("${klass.simpleName}:")
|
||||
klass.members.filterIsInstance<KProperty<*>>().sortedBy { it.name }.forEach { property ->
|
||||
if (property.name in ignoredPropertyNames) return@forEach
|
||||
val value = property.javaGetter?.invoke(symbol) ?: return@forEach
|
||||
val stringValue = renderValue(value)
|
||||
appendLine(" ${property.name}: $stringValue")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderValue(value: Any?): String = when (value) {
|
||||
null -> "null"
|
||||
is Boolean -> value.toString()
|
||||
is Name -> value.asString()
|
||||
is FqName -> value.asString()
|
||||
is ClassId -> value.asString()
|
||||
is Enum<*> -> value.name
|
||||
is List<*> -> buildString {
|
||||
append("[")
|
||||
value.joinTo(this) { renderValue(it) }
|
||||
append("]")
|
||||
}
|
||||
is TypeInfo -> value.asDenotableTypeStringRepresentation()
|
||||
is KtSymbol -> {
|
||||
val symbolTag = when (value) {
|
||||
is KtClassLikeSymbol -> renderValue(value.classId)
|
||||
is KtFunctionSymbol -> renderValue(value.fqName)
|
||||
is KtConstructorSymbol -> "<constructor>"
|
||||
is KtNamedSymbol -> renderValue(value.name)
|
||||
else -> TODO(value::class.toString())
|
||||
}
|
||||
"${value::class.simpleName!!}($symbolTag)"
|
||||
}
|
||||
else -> value::class.simpleName!!
|
||||
}
|
||||
|
||||
private val ignoredPropertyNames = setOf("fir", "psi", "token")
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
class A {
|
||||
}
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirClassOrObjectSymbol:
|
||||
classId: A
|
||||
classKind: CLASS
|
||||
name: A
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
typeParameters: []
|
||||
*/
|
||||
@@ -0,0 +1,37 @@
|
||||
class A {
|
||||
val a: Int = 10
|
||||
fun x() = 10
|
||||
}
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirPropertySymbol:
|
||||
fqName: A.a
|
||||
isExtension: false
|
||||
isVal: true
|
||||
name: a
|
||||
origin: SOURCE
|
||||
receiverType: kotlin/Int
|
||||
symbolKind: MEMBER
|
||||
type: kotlin/Int
|
||||
|
||||
KtFirFunctionSymbol:
|
||||
fqName: A.x
|
||||
isExtension: false
|
||||
isOperator: false
|
||||
isSuspend: false
|
||||
name: x
|
||||
origin: SOURCE
|
||||
symbolKind: MEMBER
|
||||
type: kotlin/Int
|
||||
typeParameters: []
|
||||
valueParameters: []
|
||||
|
||||
KtFirClassOrObjectSymbol:
|
||||
classId: A
|
||||
classKind: CLASS
|
||||
name: A
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
typeParameters: []
|
||||
*/
|
||||
@@ -0,0 +1,21 @@
|
||||
class A<T, R> {
|
||||
}
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirTypeParameterSymbol:
|
||||
name: T
|
||||
origin: SOURCE
|
||||
|
||||
KtFirTypeParameterSymbol:
|
||||
name: R
|
||||
origin: SOURCE
|
||||
|
||||
KtFirClassOrObjectSymbol:
|
||||
classId: A
|
||||
classKind: CLASS
|
||||
name: A
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
typeParameters: [KtFirTypeParameterSymbol(T), KtFirTypeParameterSymbol(R)]
|
||||
*/
|
||||
@@ -0,0 +1,17 @@
|
||||
fun String.foo(): Int = 10
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirFunctionSymbol:
|
||||
fqName: foo
|
||||
isExtension: true
|
||||
isOperator: false
|
||||
isSuspend: false
|
||||
name: foo
|
||||
origin: SOURCE
|
||||
receiverType: kotlin/String
|
||||
symbolKind: TOP_LEVEL
|
||||
type: kotlin/Int
|
||||
typeParameters: []
|
||||
valueParameters: []
|
||||
*/
|
||||
@@ -0,0 +1,23 @@
|
||||
fun foo(x: Int) {}
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirFunctionValueParameterSymbol:
|
||||
isVararg: false
|
||||
name: x
|
||||
origin: SOURCE
|
||||
symbolKind: LOCAL
|
||||
type: kotlin/Int
|
||||
|
||||
KtFirFunctionSymbol:
|
||||
fqName: foo
|
||||
isExtension: false
|
||||
isOperator: false
|
||||
isSuspend: false
|
||||
name: foo
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
type: kotlin/Unit
|
||||
typeParameters: []
|
||||
valueParameters: [KtFirFunctionValueParameterSymbol(x)]
|
||||
*/
|
||||
@@ -0,0 +1,27 @@
|
||||
fun <X> foo(x: X) {}
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirTypeParameterSymbol:
|
||||
name: X
|
||||
origin: SOURCE
|
||||
|
||||
KtFirFunctionValueParameterSymbol:
|
||||
isVararg: false
|
||||
name: x
|
||||
origin: SOURCE
|
||||
symbolKind: LOCAL
|
||||
type: X
|
||||
|
||||
KtFirFunctionSymbol:
|
||||
fqName: foo
|
||||
isExtension: false
|
||||
isOperator: false
|
||||
isSuspend: false
|
||||
name: foo
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
type: kotlin/Unit
|
||||
typeParameters: [KtFirTypeParameterSymbol(X)]
|
||||
valueParameters: [KtFirFunctionValueParameterSymbol(x)]
|
||||
*/
|
||||
@@ -0,0 +1,16 @@
|
||||
fun foo(): Int = 10
|
||||
|
||||
// SYMBOLS:
|
||||
/*
|
||||
KtFirFunctionSymbol:
|
||||
fqName: foo
|
||||
isExtension: false
|
||||
isOperator: false
|
||||
isSuspend: false
|
||||
name: foo
|
||||
origin: SOURCE
|
||||
symbolKind: TOP_LEVEL
|
||||
type: kotlin/Int
|
||||
typeParameters: []
|
||||
valueParameters: []
|
||||
*/
|
||||
+9
-39
@@ -5,28 +5,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.fir
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.editor.CaretState
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiParameter
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
import org.intellij.plugins.relaxNG.compact.psi.util.PsiFunction
|
||||
import org.jetbrains.kotlin.idea.addExternalTestFiles
|
||||
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
|
||||
import org.jetbrains.kotlin.idea.frontend.api.CallInfo
|
||||
import org.jetbrains.kotlin.idea.frontend.api.TypeInfo
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionLikeSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtParameterSymbol
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightTestCase
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
import kotlin.reflect.KProperty1
|
||||
@@ -39,7 +32,7 @@ abstract class AbstractResolveCallTest : @Suppress("DEPRECATION") KotlinLightCod
|
||||
override fun getTestDataPath(): String = KotlinTestUtils.getHomeDirectory() + "/"
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
File(path).getExternalFiles().forEach(::addFile)
|
||||
addExternalTestFiles(path)
|
||||
configureByFile(path)
|
||||
val elements = editor.caretModel.caretsAndSelections.map { selection ->
|
||||
getSingleSelectedElement(selection)
|
||||
@@ -89,29 +82,6 @@ abstract class AbstractResolveCallTest : @Suppress("DEPRECATION") KotlinLightCod
|
||||
editor.logicalPositionToOffset(selectionStart!!),
|
||||
editor.logicalPositionToOffset(selectionEnd!!)
|
||||
)
|
||||
|
||||
private fun addFile(file: File) {
|
||||
addFile(FileUtil.loadFile(file, /* convertLineSeparators = */true), file.name)
|
||||
}
|
||||
|
||||
private fun addFile(text: String, fileName: String) {
|
||||
runWriteAction {
|
||||
val virtualDir = LightPlatformTestCase.getSourceRoot()!!
|
||||
val virtualFile = virtualDir.createChildData(null, fileName)
|
||||
virtualFile.getOutputStream(null)!!.writer().use { it.write(text) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
|
||||
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
|
||||
|
||||
private fun File.getExternalFiles(): List<File> {
|
||||
val directory = parentFile
|
||||
val externalFileName = "${nameWithoutExtension}.external"
|
||||
return directory.listFiles { _, name ->
|
||||
name == "$externalFileName.kt" || name == "$externalFileName.java"
|
||||
}!!.filterNotNull()
|
||||
}
|
||||
|
||||
private fun CallInfo.stringRepresentation(): String {
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.symbols
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
|
||||
import org.jetbrains.kotlin.idea.addExternalTestFiles
|
||||
import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.FirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractSymbolsByPsiBuildingTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
protected fun doTest(path: String) {
|
||||
addExternalTestFiles(path)
|
||||
val file = File(path)
|
||||
val ktFile = myFixture.configureByText(file.name, FileUtil.loadFile(file)) as KtFile
|
||||
|
||||
val renderedSymbols = executeOnPooledThreadInReadAction {
|
||||
val analysisSession = FirAnalysisSession(ktFile)
|
||||
val declarationSymbols = ktFile.collectDescendantsOfType<KtDeclaration>().map { declaration ->
|
||||
analysisSession.symbolProvider.getSymbol(declaration)
|
||||
}
|
||||
declarationSymbols.map(DebugSymbolRenderer::render)
|
||||
}
|
||||
|
||||
val actual = buildString {
|
||||
val actualSymbolsData = renderedSymbols.joinToString(separator = "\n")
|
||||
val fileTextWithoutSymbolsData = ktFile.text.substringBeforeLast(SYMBOLS_TAG).trimEnd()
|
||||
appendLine(fileTextWithoutSymbolsData)
|
||||
appendLine()
|
||||
appendLine(SYMBOLS_TAG)
|
||||
appendLine("/*")
|
||||
append(actualSymbolsData)
|
||||
appendLine("*/")
|
||||
}
|
||||
KotlinTestUtils.assertEqualsToFile(file, actual)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SYMBOLS_TAG = "// SYMBOLS:"
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.idea.frontend.api.symbols;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/idea-frontend-fir/testData/symbolsByPsi")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class SymbolsByPsiBuildingTestGenerated extends AbstractSymbolsByPsiBuildingTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSymbolsByPsi() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/symbolsByPsi"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("class.kt")
|
||||
public void testClass() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classMembes.kt")
|
||||
public void testClassMembes() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/classMembes.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classWithTypeParams.kt")
|
||||
public void testClassWithTypeParams() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/classWithTypeParams.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/extensionFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("function.kt")
|
||||
public void testFunction() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/function.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionWithTypeParams.kt")
|
||||
public void testFunctionWithTypeParams() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/functionWithTypeParams.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitReturn.kt")
|
||||
public void testImplicitReturn() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/symbolsByPsi/implicitReturn.kt");
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,41 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea
|
||||
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.DuplicatedFirSourceElementsException
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.DuplicatedFirSourceElementsException
|
||||
import java.io.File
|
||||
|
||||
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
|
||||
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
|
||||
|
||||
fun addExternalTestFiles(testDataFilePath: String) {
|
||||
File(testDataFilePath).getExternalFiles().forEach(::addFile)
|
||||
}
|
||||
|
||||
private fun addFile(file: File) {
|
||||
addFile(FileUtil.loadFile(file, /* convertLineSeparators = */true), file.name)
|
||||
}
|
||||
|
||||
private fun addFile(text: String, fileName: String) {
|
||||
runWriteAction {
|
||||
val virtualDir = LightPlatformTestCase.getSourceRoot()!!
|
||||
val virtualFile = virtualDir.createChildData(null, fileName)
|
||||
virtualFile.getOutputStream(null)!!.writer().use { it.write(text) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.getExternalFiles(): List<File> {
|
||||
val directory = parentFile
|
||||
val externalFileName = "${nameWithoutExtension}.external"
|
||||
return directory.listFiles { _, name ->
|
||||
name == "$externalFileName.kt" || name == "$externalFileName.java"
|
||||
}!!.filterNotNull()
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary
|
||||
|
||||
Reference in New Issue
Block a user