FIR IDE: add tests for call resolve
This commit is contained in:
@@ -87,6 +87,7 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirLazyResolveTest
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
|
import org.jetbrains.kotlin.idea.fir.low.level.api.AbstractFirMultiModuleResolveTest
|
||||||
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
|
import org.jetbrains.kotlin.idea.fir.AbstractKtDeclarationAndFirDeclarationEqualityChecker
|
||||||
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
import org.jetbrains.kotlin.idea.folding.AbstractKotlinFoldingTest
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.fir.AbstractResolveCallTest
|
||||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
|
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyTest
|
||||||
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest
|
import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest
|
||||||
import org.jetbrains.kotlin.idea.highlighter.*
|
import org.jetbrains.kotlin.idea.highlighter.*
|
||||||
@@ -923,20 +924,26 @@ fun main(args: Array<String>) {
|
|||||||
testClass<AbstractKtDeclarationAndFirDeclarationEqualityChecker> {
|
testClass<AbstractKtDeclarationAndFirDeclarationEqualityChecker> {
|
||||||
model("ktDeclarationAndFirDeclarationEqualityChecker")
|
model("ktDeclarationAndFirDeclarationEqualityChecker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractResolveCallTest> {
|
||||||
|
model("analysisSession/resolveCall")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testGroup("idea/idea-fir/tests", "idea/testData") {
|
testGroup("idea/idea-frontend-fir/idea-fir-low-level-api/tests", "idea/testData") {
|
||||||
testClass<AbstractFirMultiModuleResolveTest> {
|
testClass<AbstractFirMultiModuleResolveTest> {
|
||||||
model("fir/multiModule", recursive = false, extension = null)
|
model("fir/multiModule", recursive = false, extension = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractFirHighlightingTest> {
|
|
||||||
model("highlighter")
|
|
||||||
}
|
|
||||||
|
|
||||||
testClass<AbstractFirLazyResolveTest> {
|
testClass<AbstractFirLazyResolveTest> {
|
||||||
model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testGroup("idea/idea-fir/tests", "idea/testData") {
|
||||||
|
testClass<AbstractFirHighlightingTest> {
|
||||||
|
model("highlighter")
|
||||||
|
}
|
||||||
|
|
||||||
testClass<AbstractFirReferenceResolveTest> {
|
testClass<AbstractFirReferenceResolveTest> {
|
||||||
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
|
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ dependencies {
|
|||||||
compile(project(":idea:idea-core"))
|
compile(project(":idea:idea-core"))
|
||||||
// </temp>
|
// </temp>
|
||||||
|
|
||||||
|
testCompile(intellijDep())
|
||||||
|
testCompile(intellijCoreDep())
|
||||||
testCompile(toolsJar())
|
testCompile(toolsJar())
|
||||||
testCompile(projectTests(":idea"))
|
testCompile(projectTests(":idea"))
|
||||||
testCompile(projectTests(":compiler:tests-common"))
|
testCompile(projectTests(":compiler:tests-common"))
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
|||||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@TestMetadata("idea/testData/fir/lazyResolve")
|
@TestMetadata("idea/testData/fir/lazyResolve")
|
||||||
@TestDataPath("/")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class FirLazyResolveTestGenerated extends AbstractFirLazyResolveTest {
|
public class FirLazyResolveTestGenerated extends AbstractFirLazyResolveTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
|||||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@TestMetadata("idea/testData/fir/multiModule")
|
@TestMetadata("idea/testData/fir/multiModule")
|
||||||
@TestDataPath("/")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleResolveTest {
|
public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleResolveTest {
|
||||||
private void runTest(String testDataFilePath) throws Exception {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
|||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun function(a: Int) {}
|
||||||
|
|
||||||
|
fun call() {
|
||||||
|
<selection>function(1)</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(a: Int): IMPLICIT_TYPE
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun String.function(a: Int) {}
|
||||||
|
|
||||||
|
fun call() {
|
||||||
|
"str".<selection>function(1)</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
fun String.function(a: Int) {}
|
||||||
|
|
||||||
|
fun call() {
|
||||||
|
"str"?.<selection>function(1)</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class A
|
||||||
|
|
||||||
|
fun call() {
|
||||||
|
val a = <selection>A()</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: KtImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
|
||||||
idea/idea-frontend-fir/testData/analysisSession/resolveCall/implicitJavaConstuctorCall.external.java
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class A {
|
||||||
|
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
fun call() {
|
||||||
|
val a = <selection>A()</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: JavaImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class JavaClass {
|
||||||
|
void javaMethod() {}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
fun call() {
|
||||||
|
val javaClass = JavaClass()
|
||||||
|
javaClass.<selection>javaMethod()</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: SimpleJavaFunctionCallInfo: targetFunction = JavaClass.javaMethod(): void
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun call(x: (Int) -> String) {
|
||||||
|
<selection>x(1)</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: VariableAsFunctionCallInfo: target = x, isSuspendCall = false
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
operator fun Int.invoke(): String {}
|
||||||
|
|
||||||
|
fun call(x: Int) {
|
||||||
|
<selection>x()</selection>
|
||||||
|
}
|
||||||
|
|
||||||
|
// CALL: SimpleKtFunctionCallInfo: targetFunction = invoke(<receiver> : Int): String
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* 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.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.frontend.api.CallInfo
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
|
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.psiUtil.elementsInRange
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.reflect.KProperty1
|
||||||
|
import kotlin.reflect.full.memberProperties
|
||||||
|
import kotlin.reflect.full.primaryConstructor
|
||||||
|
import kotlin.reflect.jvm.javaGetter
|
||||||
|
|
||||||
|
|
||||||
|
abstract class AbstractResolveCallTest : @Suppress("DEPRECATION") KotlinLightCodeInsightTestCase() {
|
||||||
|
override fun getTestDataPath(): String = KotlinTestUtils.getHomeDirectory() + "/"
|
||||||
|
|
||||||
|
protected fun doTest(path: String) {
|
||||||
|
File(path).getExternalFiles().forEach(::addFile)
|
||||||
|
configureByFile(path)
|
||||||
|
val elements = editor.caretModel.caretsAndSelections.map { selection ->
|
||||||
|
getSingleSelectedElement(selection)
|
||||||
|
}
|
||||||
|
val callInfos = executeOnPooledThreadInReadAction {
|
||||||
|
val analysisSession = FirAnalysisSession(file as KtFile)
|
||||||
|
elements.map { element ->
|
||||||
|
when (element) {
|
||||||
|
is KtCallExpression -> analysisSession.resolveCall(element)
|
||||||
|
is KtBinaryExpression -> analysisSession.resolveCall(element)
|
||||||
|
else -> error("Selected should be either KtCallExpression or KtBinaryExpression but was $element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (callInfos.isEmpty()) {
|
||||||
|
error("There are should be at least one call selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
val textWithoutLatestComments = run {
|
||||||
|
val rawText = File(path).readText()
|
||||||
|
"""(?m)^// CALL:\s.*$""".toRegex().replace(rawText, "").trimEnd()
|
||||||
|
}
|
||||||
|
val actualText = buildString {
|
||||||
|
append(textWithoutLatestComments)
|
||||||
|
append("\n\n")
|
||||||
|
callInfos.joinTo(this, separator = "\n") { info ->
|
||||||
|
"// CALL: ${info?.stringRepresentation()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KotlinTestUtils.assertEqualsToFile(File(path), actualText)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getSingleSelectedElement(selection: CaretState): PsiElement {
|
||||||
|
val selectionRange = selection.getTextRange()
|
||||||
|
val elements = file.elementsInRange(selectionRange)
|
||||||
|
if (elements.size != 1) {
|
||||||
|
val selectionText = file.text.substring(selectionRange.startOffset, selectionRange.endOffset)
|
||||||
|
error("Single element should be found for selection `$selectionText`, but $elements were found")
|
||||||
|
}
|
||||||
|
return elements.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CaretState.getTextRange() = TextRange.create(
|
||||||
|
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 {
|
||||||
|
fun Any.stringValue(): String? = when (this) {
|
||||||
|
is PsiMethod -> buildString {
|
||||||
|
append(getKotlinFqName()!!)
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
parameters.joinTo(this, prefix = "(", postfix = ")") { parameter ->
|
||||||
|
"${parameter.name}: ${(parameter as PsiParameter).typeElement!!.text}"
|
||||||
|
}
|
||||||
|
append(": ${returnTypeElement!!.text}")
|
||||||
|
}
|
||||||
|
is KtFunction -> buildString {
|
||||||
|
append(getKotlinFqName()!!)
|
||||||
|
append("(")
|
||||||
|
receiverTypeReference?.let { receiver ->
|
||||||
|
append("<receiver> : ${receiver.text}")
|
||||||
|
if (valueParameters.isNotEmpty()) append(", ")
|
||||||
|
}
|
||||||
|
valueParameters.joinTo(this,) { parameter ->
|
||||||
|
"${parameter.name}: ${parameter.typeReference!!.text}"
|
||||||
|
}
|
||||||
|
append(")")
|
||||||
|
append(": ${typeReference?.text ?: "IMPLICIT_TYPE"}")
|
||||||
|
}
|
||||||
|
is KtClass -> "Implicit constructor of ${getKotlinFqName()!!}"
|
||||||
|
is PsiClass -> "Implicit constructor of ${getKotlinFqName()!!}"
|
||||||
|
is KtParameter -> name!!
|
||||||
|
is Boolean -> toString()
|
||||||
|
else -> error("unexpected parameter type ${this::class}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val callInfoClass = this::class
|
||||||
|
return buildString {
|
||||||
|
append(callInfoClass.simpleName!!)
|
||||||
|
append(": ")
|
||||||
|
val propertyByName = callInfoClass.memberProperties.associateBy(KProperty1<*, *>::name)
|
||||||
|
callInfoClass.primaryConstructor!!.parameters.joinTo(this) { parameter ->
|
||||||
|
val value = propertyByName[parameter.name]!!.javaGetter!!(this@stringRepresentation)?.stringValue()
|
||||||
|
"${parameter.name!!} = $value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.fir;
|
||||||
|
|
||||||
|
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/analysisSession/resolveCall")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public class ResolveCallTestGenerated extends AbstractResolveCallTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInResolveCall() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-frontend-fir/testData/analysisSession/resolveCall"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCallInTheSameFile.kt")
|
||||||
|
public void testFunctionCallInTheSameFile() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/functionCallInTheSameFile.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionWithReceiverCall.kt")
|
||||||
|
public void testFunctionWithReceiverCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/functionWithReceiverCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionWithReceiverSafeCall.kt")
|
||||||
|
public void testFunctionWithReceiverSafeCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/functionWithReceiverSafeCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitConstuctorCall.kt")
|
||||||
|
public void testImplicitConstuctorCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/implicitConstuctorCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitJavaConstuctorCall.kt")
|
||||||
|
public void testImplicitJavaConstuctorCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/implicitJavaConstuctorCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaFunctionCall.kt")
|
||||||
|
public void testJavaFunctionCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/javaFunctionCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("variableAsFunction.kt")
|
||||||
|
public void testVariableAsFunction() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/variableAsFunction.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("variableAsFunctionLikeCall.kt")
|
||||||
|
public void testVariableAsFunctionLikeCall() throws Exception {
|
||||||
|
runTest("idea/idea-frontend-fir/testData/analysisSession/resolveCall/variableAsFunctionLikeCall.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user