提交 fb8acf8c 编写于 作者: I Ilya Kirillov

FIR IDE: add tests for call resolve

上级 8ac0466e
......@@ -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.AbstractKtDeclarationAndFirDeclarationEqualityChecker
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.AbstractHierarchyWithLibTest
import org.jetbrains.kotlin.idea.highlighter.*
......@@ -923,20 +924,26 @@ fun main(args: Array<String>) {
testClass<AbstractKtDeclarationAndFirDeclarationEqualityChecker> {
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> {
model("fir/multiModule", recursive = false, extension = null)
}
testClass<AbstractFirHighlightingTest> {
model("highlighter")
}
testClass<AbstractFirLazyResolveTest> {
model("fir/lazyResolve", extension = "test", singleClass = true, filenameStartsLowerCase = true)
}
}
testGroup("idea/idea-fir/tests", "idea/testData") {
testClass<AbstractFirHighlightingTest> {
model("highlighter")
}
testClass<AbstractFirReferenceResolveTest> {
model("resolve/references", pattern = KT_WITHOUT_DOTS_IN_NAME)
......
......@@ -21,6 +21,8 @@ dependencies {
compile(project(":idea:idea-core"))
// </temp>
testCompile(intellijDep())
testCompile(intellijCoreDep())
testCompile(toolsJar())
testCompile(projectTests(":idea"))
testCompile(projectTests(":compiler:tests-common"))
......
......@@ -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 */
@SuppressWarnings("all")
@TestMetadata("idea/testData/fir/lazyResolve")
@TestDataPath("/")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class FirLazyResolveTestGenerated extends AbstractFirLazyResolveTest {
private void runTest(String testDataFilePath) throws Exception {
......
......@@ -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 */
@SuppressWarnings("all")
@TestMetadata("idea/testData/fir/multiModule")
@TestDataPath("/")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class FirMultiModuleResolveTestGenerated extends AbstractFirMultiModuleResolveTest {
private void runTest(String testDataFilePath) throws Exception {
......
fun function(a: Int) {}
fun call() {
<selection>function(1)</selection>
}
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(a: Int): IMPLICIT_TYPE
\ No newline at end of file
fun String.function(a: Int) {}
fun call() {
"str".<selection>function(1)</selection>
}
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
\ No newline at end of file
fun String.function(a: Int) {}
fun call() {
"str"?.<selection>function(1)</selection>
}
// CALL: SimpleKtFunctionCallInfo: targetFunction = function(<receiver> : String, a: Int): IMPLICIT_TYPE
\ No newline at end of file
class A
fun call() {
val a = <selection>A()</selection>
}
// CALL: KtImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
\ No newline at end of file
fun call() {
val a = <selection>A()</selection>
}
// CALL: JavaImplicitPrimaryConstructorCallInfo: owner = Implicit constructor of A
\ No newline at end of file
class JavaClass {
void javaMethod() {}
}
\ No newline at end of file
fun call() {
val javaClass = JavaClass()
javaClass.<selection>javaMethod()</selection>
}
// CALL: SimpleJavaFunctionCallInfo: targetFunction = JavaClass.javaMethod(): void
\ No newline at end of file
fun call(x: (Int) -> String) {
<selection>x(1)</selection>
}
// CALL: VariableAsFunctionCallInfo: target = x, isSuspendCall = false
\ No newline at end of file
operator fun Int.invoke(): String {}
fun call(x: Int) {
<selection>x()</selection>
}
// CALL: SimpleKtFunctionCallInfo: targetFunction = invoke(<receiver> : Int): String
\ No newline at end of file
/*
* 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"
}
}
}
/*
* 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");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册