353 lines
13 KiB
Kotlin
353 lines
13 KiB
Kotlin
/*
|
|
* Copyright 2010-2018 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.psi.*
|
|
import com.intellij.testFramework.LightProjectDescriptor
|
|
import junit.framework.TestCase
|
|
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
|
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
|
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|
import org.jetbrains.uast.*
|
|
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
|
|
import org.jetbrains.uast.test.env.kotlin.findElementByText
|
|
import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi
|
|
import org.jetbrains.uast.test.env.kotlin.findUElementByTextFromPsi
|
|
import org.junit.runner.RunWith
|
|
|
|
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
|
class KotlinUastResolveApiTest : KotlinLightCodeInsightFixtureTestCase() {
|
|
|
|
override fun getProjectDescriptor(): LightProjectDescriptor =
|
|
KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
|
|
|
|
|
fun testResolveStringFromUast() {
|
|
val file = myFixture.addFileToProject(
|
|
"s.kt", """fun foo(){
|
|
val s = "abc"
|
|
s.toUpperCase()
|
|
}
|
|
""${'"'}"""
|
|
)
|
|
|
|
val refs = file.findUElementByTextFromPsi<UQualifiedReferenceExpression>("s.toUpperCase()")
|
|
val receiver = refs.receiver
|
|
TestCase.assertEquals(CommonClassNames.JAVA_LANG_STRING, (receiver.getExpressionType() as PsiClassType).resolve()!!.qualifiedName!!)
|
|
val resolve = receiver.cast<UReferenceExpression>().resolve()
|
|
|
|
val variable = file.findUElementByTextFromPsi<UVariable>("val s = \"abc\"")
|
|
TestCase.assertEquals(resolve, variable.javaPsi)
|
|
TestCase.assertTrue(
|
|
"resolved expression $resolve should be equivalent to ${variable.sourcePsi}",
|
|
PsiManager.getInstance(project).areElementsEquivalent(resolve, variable.sourcePsi)
|
|
)
|
|
}
|
|
|
|
fun testMultiResolve() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
fun foo(): Int = TODO()
|
|
fun foo(a: Int): Int = TODO()
|
|
fun foo(a: Int, b: Int): Int = TODO()
|
|
|
|
|
|
fun main(args: Array<String>) {
|
|
foo(1<caret>
|
|
}"""
|
|
)
|
|
|
|
val main = file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
val functionCall =
|
|
main.findElementByText<UElement>("foo").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"fun foo(): Int = TODO()",
|
|
"fun foo(a: Int): Int = TODO()",
|
|
"fun foo(a: Int, b: Int): Int = TODO()"
|
|
)
|
|
|
|
TestCase.assertEquals(PsiType.INT, functionCall.getExpressionType())
|
|
|
|
val firstArgument = main.findElementByText<UElement>("1")
|
|
val firstParameter = functionCall.getArgumentForParameter(0)
|
|
TestCase.assertEquals(firstArgument, firstParameter)
|
|
|
|
}
|
|
|
|
fun testMultiResolveJava() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
|
|
fun main(args: Array<String>) {
|
|
System.out.print("1"
|
|
}
|
|
"""
|
|
)
|
|
|
|
val main = file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
val functionCall = main.findElementByText<UElement>("print").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"public void print(char c) { /* compiled code */ }",
|
|
"public void print(int i) { /* compiled code */ }",
|
|
"public void print(long l) { /* compiled code */ }",
|
|
"public void print(float f) { /* compiled code */ }",
|
|
"public void print(double d) { /* compiled code */ }",
|
|
"public void print(char[] s) { /* compiled code */ }",
|
|
"public void print(java.lang.String s) { /* compiled code */ }",
|
|
"public void print(java.lang.Object obj) { /* compiled code */ }"
|
|
)
|
|
|
|
TestCase.assertEquals(PsiType.VOID, functionCall.getExpressionType())
|
|
|
|
val firstArgument = main.findElementByText<UElement>("1")
|
|
val firstParameter = functionCall.getArgumentForParameter(0)
|
|
TestCase.assertEquals(firstArgument, firstParameter)
|
|
}
|
|
|
|
fun testMultiResolveJavaAmbiguous() {
|
|
myFixture.addClass(
|
|
"""
|
|
public class JavaClass {
|
|
|
|
public void setParameter(String name, int value){}
|
|
public void setParameter(String name, double value){}
|
|
public void setParameter(String name, String value){}
|
|
|
|
}
|
|
"""
|
|
)
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
|
|
fun main(args: Array<String>) {
|
|
JavaClass().setParameter("1"
|
|
}
|
|
"""
|
|
)
|
|
|
|
val main = file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
val functionCall = main.findElementByText<UElement>("setParameter").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"public void setParameter(String name, int value){}",
|
|
"public void setParameter(String name, double value){}",
|
|
"public void setParameter(String name, String value){}"
|
|
|
|
)
|
|
|
|
TestCase.assertEquals(PsiType.VOID, functionCall.getExpressionType())
|
|
|
|
val firstArgument = main.findElementByText<UElement>("1")
|
|
val firstParameter = functionCall.getArgumentForParameter(0)
|
|
TestCase.assertEquals(firstArgument, firstParameter)
|
|
}
|
|
|
|
fun testMultiResolveInClass() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
class MyClass {
|
|
|
|
fun foo(): Int = TODO()
|
|
fun foo(a: Int): Int = TODO()
|
|
fun foo(a: Int, b: Int): Int = TODO()
|
|
|
|
}
|
|
|
|
fun foo(string: String) = TODO()
|
|
|
|
|
|
fun main(args: Array<String>) {
|
|
MyClass().foo(
|
|
}
|
|
"""
|
|
)
|
|
|
|
|
|
val functionCall =
|
|
file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
.findElementByText<UElement>("foo").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"fun foo(): Int = TODO()",
|
|
"fun foo(a: Int): Int = TODO()",
|
|
"fun foo(a: Int, b: Int): Int = TODO()"
|
|
)
|
|
assertDoesntContain(resolvedDeclarationsStrings, "fun foo(string: String) = TODO()")
|
|
TestCase.assertEquals(PsiType.INT, functionCall.getExpressionType())
|
|
}
|
|
|
|
fun testMultiConstructorResolve() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
class MyClass(int: Int) {
|
|
|
|
constructor(int: Int, int1: Int) : this(int + int1)
|
|
|
|
fun foo(): Int = TODO()
|
|
|
|
}
|
|
|
|
fun MyClass(string: String): MyClass = MyClass(1)
|
|
|
|
|
|
fun main(args: Array<String>) {
|
|
MyClass(
|
|
}
|
|
"""
|
|
)
|
|
|
|
|
|
val functionCall =
|
|
file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
.findElementByText<UElement>("MyClass").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"(int: Int)",
|
|
"constructor(int: Int, int1: Int) : this(int + int1)",
|
|
"fun MyClass(string: String): MyClass = MyClass(1)"
|
|
)
|
|
assertDoesntContain(resolvedDeclarationsStrings, "fun foo(): Int = TODO()")
|
|
TestCase.assertEquals(PsiType.getTypeByName("MyClass", project, file.resolveScope), functionCall.getExpressionType())
|
|
}
|
|
|
|
|
|
fun testMultiInvokableObjectResolve() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
object Foo {
|
|
|
|
operator fun invoke(i: Int): Int = TODO()
|
|
operator fun invoke(i1: Int, i2: Int): Int = TODO()
|
|
operator fun invoke(i1: Int, i2: Int, i3: Int): Int = TODO()
|
|
|
|
}
|
|
|
|
fun main(args: Array<String>) {
|
|
Foo(
|
|
}
|
|
"""
|
|
)
|
|
|
|
val functionCall =
|
|
file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
.findElementByText<UElement>("Foo").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"operator fun invoke(i: Int): Int = TODO()",
|
|
"operator fun invoke(i1: Int, i2: Int): Int = TODO()",
|
|
"operator fun invoke(i1: Int, i2: Int, i3: Int): Int = TODO()"
|
|
)
|
|
TestCase.assertEquals(PsiType.INT, functionCall.getExpressionType())
|
|
}
|
|
|
|
fun testMultiResolveJvmOverloads() {
|
|
val file = myFixture.configureByText(
|
|
"s.kt", """
|
|
|
|
class MyClass {
|
|
|
|
@JvmOverloads
|
|
fun foo(i1: Int = 1, i2: Int = 2): Int = TODO()
|
|
|
|
}
|
|
|
|
fun main(args: Array<String>) {
|
|
MyClass().foo(
|
|
}"""
|
|
)
|
|
|
|
val functionCall =
|
|
file.toUElement()!!.findElementByTextFromPsi<UElement>("main").getContainingUMethod()!!
|
|
.findElementByText<UElement>("foo").uastParent as KotlinUFunctionCallExpression
|
|
|
|
val resolvedDeclaration = functionCall.multiResolve()
|
|
val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "<null>" }
|
|
assertContainsElements(
|
|
resolvedDeclarationsStrings,
|
|
"@JvmOverloads\n fun foo(i1: Int = 1, i2: Int = 2): Int = TODO()"
|
|
)
|
|
TestCase.assertEquals(PsiType.INT, functionCall.getExpressionType())
|
|
}
|
|
|
|
fun testLocalResolve() {
|
|
myFixture.configureByText(
|
|
"MyClass.kt", """
|
|
fun foo() {
|
|
fun bar() {}
|
|
|
|
ba<caret>r()
|
|
}
|
|
"""
|
|
)
|
|
|
|
|
|
val uCallExpression = myFixture.file.findElementAt(myFixture.caretOffset).toUElement().getUCallExpression().orFail("cant convert to UCallExpression")
|
|
val resolved = uCallExpression.resolve().orFail("cant resolve from $uCallExpression")
|
|
TestCase.assertEquals("bar", resolved.name)
|
|
}
|
|
|
|
|
|
fun testResolveCompiledAnnotation() {
|
|
myFixture.configureByText(
|
|
"MyClass.kt", """
|
|
@Deprecated(message = "deprecated")
|
|
fun foo() {}
|
|
"""
|
|
)
|
|
|
|
val compiledAnnotationParameter = myFixture.file.toUElement()!!.findElementByTextFromPsi<USimpleNameReferenceExpression>("message")
|
|
val resolved = compiledAnnotationParameter.resolve() as PsiMethod
|
|
TestCase.assertEquals("message", resolved.name)
|
|
}
|
|
|
|
fun testAssigningArrayElementType() {
|
|
myFixture.configureByText(
|
|
"MyClass.kt", """
|
|
fun foo() {
|
|
val arr = arrayOfNulls<List<*>>(10)
|
|
arr[0] = emptyList<Any>()
|
|
|
|
val lst = mutableListOf<List<*>>()
|
|
lst[0] = emptyList<Any>()
|
|
}
|
|
"""
|
|
)
|
|
|
|
val uFile = myFixture.file.toUElement()!!
|
|
|
|
TestCase.assertEquals(
|
|
"PsiType:List<?>",
|
|
uFile.findElementByTextFromPsi<UExpression>("arr[0]").getExpressionType().toString()
|
|
)
|
|
TestCase.assertEquals(
|
|
"PsiType:List<?>",
|
|
uFile.findElementByTextFromPsi<UExpression>("lst[0]").getExpressionType().toString()
|
|
)
|
|
}
|
|
}
|
|
|