FIR UAST: running resolve API tests for both plugins

This commit is contained in:
Jinseong Jeon
2021-06-16 00:28:23 -07:00
committed by TeamCityServer
parent 610b68c29d
commit bc09d94717
4 changed files with 166 additions and 86 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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 }
}