From bc09d947174bfc3fc9f3518d34282f824e5b66d9 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Wed, 16 Jun 2021 00:28:23 -0700 Subject: [PATCH] FIR UAST: running resolve API tests for both plugins --- .../common/kotlin/UastResolveApiTestBase.kt | 111 ++++++++++++++++++ .../uast/test/kotlin/FE1UastResolveApiTest.kt | 49 ++++++++ .../uast/test/kotlin/FirUastResolveApiTest.kt | 90 +------------- .../org/jetbrains/uast/test/kotlin/utils.kt | 2 +- 4 files changed, 166 insertions(+), 86 deletions(-) create mode 100644 plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/common/kotlin/UastResolveApiTestBase.kt create mode 100644 plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FE1UastResolveApiTest.kt diff --git a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/common/kotlin/UastResolveApiTestBase.kt b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/common/kotlin/UastResolveApiTestBase.kt new file mode 100644 index 00000000000..400be7dd7a3 --- /dev/null +++ b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/common/kotlin/UastResolveApiTestBase.kt @@ -0,0 +1,111 @@ +/* + * 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.uast.test.common.kotlin + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMethod +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.psi.KtImportDirective +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.uast.UCallableReferenceExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UFile +import org.jetbrains.uast.UThisExpression +import org.jetbrains.uast.test.kotlin.findFacade +import org.jetbrains.uast.visitor.UastVisitor +import org.junit.Assert +import java.lang.IllegalStateException + +interface UastResolveApiTestBase : FirUastPluginSelection { + fun checkCallbackForMethodReference(filePath: String, uFile: UFile) { + val facade = uFile.findFacade() + ?: throw IllegalStateException("No facade found at ${uFile.asRefNames()}") + // val x = Foo::bar + val x = facade.fields.single() + var barReference: PsiElement? = null + x.accept(object : UastVisitor { + override fun visitElement(node: UElement): Boolean { + return false + } + + override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean { + barReference = node.resolve() + return false + } + }) + Assert.assertNotNull("Foo::bar is not resolved", barReference) + if (!isFirUastPlugin) { + // TODO: FIR UAST doesn't need this unwrapping. Is this a breaking change? + barReference = (barReference as KtLightMethod).kotlinOrigin + } + Assert.assertTrue("Foo::bar is not a function", barReference is KtNamedFunction) + Assert.assertEquals("Foo.bar", (barReference as KtNamedFunction).fqName?.asString()) + } + + fun checkCallbackForImports(filePath: String, uFile: UFile) { + uFile.imports.forEach { uImport -> + if ((uImport.sourcePsi as? KtImportDirective)?.text?.endsWith("sleep") == true) { + // There are two static [sleep] in [java.lang.Thread], so the import (w/o knowing its usage) can't be resolved to + // a single function, hence `null` (as [resolve] result). + // TODO: make [UImportStatement] a subtype of [UMultiResolvable], instead of [UResolvable]? + return@forEach + } + val resolvedImport = uImport.resolve() + ?: throw IllegalStateException("Unresolved import: ${uImport.asRenderString()}") + val expected = when (resolvedImport) { + is PsiClass -> { + // import java.lang.Thread.* + resolvedImport.name == "Thread" || resolvedImport.name == "UncaughtExceptionHandler" + } + is PsiMethod -> { + // import java.lang.Thread.currentThread + resolvedImport.name == "currentThread" || + // import kotlin.collections.emptyList + (!isFirUastPlugin && resolvedImport.name == "emptyList") + } + is PsiField -> { + // import java.lang.Thread.NORM_PRIORITY + resolvedImport.name == "NORM_PRIORITY" || + // import kotlin.Int.Companion.SIZE_BYTES + (!isFirUastPlugin && resolvedImport.name == "SIZE_BYTES") + } + is KtNamedFunction -> { + // import kotlin.collections.emptyList + isFirUastPlugin && resolvedImport.isTopLevel && resolvedImport.name == "emptyList" + } + is KtProperty -> { + // import kotlin.Int.Companion.SIZE_BYTES + isFirUastPlugin && resolvedImport.name == "SIZE_BYTES" + } + else -> false + } + Assert.assertTrue("Unexpected import: $resolvedImport", expected) + } + } + + fun checkCallbackForReceiverFun(filePath: String, uFile: UFile) { + val facade = uFile.findFacade() + ?: throw IllegalStateException("No facade found at ${uFile.asRefNames()}") + // ... String.foo() = this.length + val foo = facade.methods.find { it.name == "foo" } + ?: throw IllegalStateException("Target function not found at ${uFile.asRefNames()}") + var thisReference: PsiElement? = foo + foo.accept(object : UastVisitor { + override fun visitElement(node: UElement): Boolean { + return false + } + + override fun visitThisExpression(node: UThisExpression): Boolean { + thisReference = node.resolve() + return false + } + }) + Assert.assertNull("plain `this` has `null` label", thisReference) + } +} diff --git a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FE1UastResolveApiTest.kt b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FE1UastResolveApiTest.kt new file mode 100644 index 00000000000..29a6ef29be1 --- /dev/null +++ b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FE1UastResolveApiTest.kt @@ -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.uast.test.kotlin + +import com.intellij.testFramework.TestDataPath +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners +import org.jetbrains.kotlin.test.TestMetadata +import org.jetbrains.uast.UFile +import org.jetbrains.uast.test.common.kotlin.UastResolveApiTestBase +import org.jetbrains.uast.test.env.kotlin.AbstractFE1UastTest +import org.junit.runner.RunWith +import java.io.File + +@RunWith(JUnit3RunnerWithInners::class) +class FE1UastResolveApiTest : AbstractFE1UastTest() { + override fun check(testName: String, file: UFile) { + // Bogus + } + + @TestMetadata("plugins/uast-kotlin/testData") + @TestDataPath("\$PROJECT_ROOT") + class Legacy : AbstractFE1UastTest(), UastResolveApiTestBase { + override var testDataDir = File("plugins/uast-kotlin/testData") + + override val isFirUastPlugin: Boolean = false + + override fun check(testName: String, file: UFile) { + // Bogus + } + + @TestMetadata("MethodReference.kt") + fun testMethodReference() { + doTest("MethodReference", ::checkCallbackForMethodReference) + } + + @TestMetadata("Imports.kt") + fun testImports() { + doTest("Imports", ::checkCallbackForImports) + } + + @TestMetadata("ReceiverFun.kt") + fun testReceiverFun() { + doTest("ReceiverFun", ::checkCallbackForReceiverFun) + } + } +} diff --git a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FirUastResolveApiTest.kt b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FirUastResolveApiTest.kt index 0ef10dad103..65f96b3acdb 100644 --- a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FirUastResolveApiTest.kt +++ b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/FirUastResolveApiTest.kt @@ -5,21 +5,13 @@ package org.jetbrains.uast.test.kotlin -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiField -import com.intellij.psi.PsiMethod import com.intellij.testFramework.TestDataPath -import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.test.JUnit3RunnerWithInners import org.jetbrains.kotlin.test.TestMetadata import org.jetbrains.uast.* -import org.jetbrains.uast.test.common.kotlin.asRefNames +import org.jetbrains.uast.test.common.kotlin.UastResolveApiTestBase import org.jetbrains.uast.test.env.kotlin.AbstractFirUastTest -import org.jetbrains.uast.visitor.UastVisitor -import org.junit.Assert import org.junit.runner.RunWith -import java.lang.IllegalStateException @RunWith(JUnit3RunnerWithInners::class) class FirUastResolveApiTest : AbstractFirUastTest() { @@ -34,7 +26,7 @@ class FirUastResolveApiTest : AbstractFirUastTest() { @TestMetadata("plugins/uast-kotlin/testData") @TestDataPath("\$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners::class) - class Legacy : AbstractFirUastTest() { + class Legacy : AbstractFirUastTest(), UastResolveApiTestBase { override val isFirUastPlugin: Boolean = true override fun check(filePath: String, file: UFile) { @@ -43,89 +35,17 @@ class FirUastResolveApiTest : AbstractFirUastTest() { @TestMetadata("MethodReference.kt") fun testMethodReference() { - doCheck("plugins/uast-kotlin/testData/MethodReference.kt") { _, uFile -> - val facade = uFile.findFacade() - ?: throw IllegalStateException("No facade found at ${uFile.asRefNames()}") - // val x = Foo::bar - val x = facade.fields.single() - var barReference: PsiElement? = null - x.accept(object : UastVisitor { - override fun visitElement(node: UElement): Boolean { - return false - } - - override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean { - barReference = node.resolve() - return false - } - }) - Assert.assertNotNull("Foo::bar is not resolved", barReference) - Assert.assertTrue("Foo::bar is not a function", barReference is KtNamedFunction) - Assert.assertEquals("Foo.bar", (barReference as KtNamedFunction).fqName?.asString()) - } + doCheck("plugins/uast-kotlin/testData/MethodReference.kt", ::checkCallbackForMethodReference) } @TestMetadata("Imports.kt") fun testImports() { - doCheck("plugins/uast-kotlin/testData/Imports.kt") { _, uFile -> - uFile.imports.forEach { uImport -> - if ((uImport.sourcePsi as? KtImportDirective)?.text?.endsWith("sleep") == true) { - // There are two static [sleep] in [java.lang.Thread], so the import (w/o knowing its usage) can't be resolved to - // a single function, hence `null` (as [resolve] result). - // TODO: make [UImportStatement] a subtype of [UMultiResolvable], instead of [UResolvable]? - return@forEach - } - val resolvedImport = uImport.resolve() - ?: throw IllegalStateException("Unresolved import: ${uImport.asRenderString()}") - val expected = when (resolvedImport) { - is PsiClass -> { - // import java.lang.Thread.* - resolvedImport.name == "Thread" || resolvedImport.name == "UncaughtExceptionHandler" - } - is PsiMethod -> { - // import java.lang.Thread.currentThread - resolvedImport.name == "currentThread" - } - is PsiField -> { - // import java.lang.Thread.NORM_PRIORITY - resolvedImport.name == "NORM_PRIORITY" - } - is KtNamedFunction -> { - // import kotlin.collections.emptyList - resolvedImport.isTopLevel && resolvedImport.name == "emptyList" - } - is KtProperty -> { - // import kotlin.Int.Companion.SIZE_BYTES - resolvedImport.name == "SIZE_BYTES" - } - else -> false - } - Assert.assertTrue("Unexpected import: $resolvedImport", expected) - } - } + doCheck("plugins/uast-kotlin/testData/Imports.kt", ::checkCallbackForImports) } @TestMetadata("ReceiverFun.kt") fun testReceiverFun() { - doCheck("plugins/uast-kotlin/testData/ReceiverFun.kt") { _, uFile -> - val facade = uFile.findFacade() - ?: throw IllegalStateException("No facade found at ${uFile.asRefNames()}") - // ... String.foo() = this.length - val foo = facade.methods.find { it.name == "foo" } - ?: throw IllegalStateException("Target function not found at ${uFile.asRefNames()}") - var thisReference: PsiElement? = foo - foo.accept(object : UastVisitor { - override fun visitElement(node: UElement): Boolean { - return false - } - - override fun visitThisExpression(node: UThisExpression): Boolean { - thisReference = node.resolve() - return false - } - }) - Assert.assertNull("plain `this` has `null` label", thisReference) - } + doCheck("plugins/uast-kotlin/testData/ReceiverFun.kt", ::checkCallbackForReceiverFun) } } } diff --git a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/utils.kt b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/utils.kt index 28091580ef3..cdf0a955abd 100644 --- a/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/utils.kt +++ b/plugins/uast-kotlin-fir/tests/org/jetbrains/uast/test/kotlin/utils.kt @@ -10,5 +10,5 @@ import org.jetbrains.uast.UClass import org.jetbrains.uast.UFile internal fun UFile.findFacade(): UClass? { - return classes.find { it.sourcePsi is KtLightClassForFacade } + return classes.find { it.psi is KtLightClassForFacade } }