181: Uast: getArgumentForParameter(i: Int) implementation for Kotlin (IDEA-184046)
This commit is contained in:
committed by
Nikolay Krasko
parent
1507eed1cf
commit
e48b905075
+29
-6
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtCallElement
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -40,7 +37,7 @@ class KotlinUFunctionCallExpression(
|
||||
override val psi: KtCallElement,
|
||||
givenParent: UElement?,
|
||||
private val _resolvedCall: ResolvedCall<*>?
|
||||
) : KotlinAbstractUExpression(givenParent), UCallExpression, KotlinUElementWithType {
|
||||
) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, KotlinUElementWithType {
|
||||
companion object {
|
||||
fun resolveSource(descriptor: DeclarationDescriptor, source: PsiElement?): PsiMethod? {
|
||||
if (descriptor is ConstructorDescriptor && descriptor.isPrimary
|
||||
@@ -85,6 +82,29 @@ class KotlinUFunctionCallExpression(
|
||||
|
||||
override val valueArguments by lz { psi.valueArguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } }
|
||||
|
||||
override fun getArgumentForParameter(i: Int): UExpression? {
|
||||
val resolvedCall = resolvedCall ?: return null
|
||||
val actualParamIndex = if (resolvedCall.extensionReceiver == null) i else i - 1
|
||||
if (actualParamIndex == -1) return receiver
|
||||
val (parameter, resolvedArgument) = resolvedCall.valueArguments.entries.find { it.key.index == actualParamIndex } ?: return null
|
||||
val arguments = resolvedArgument.arguments
|
||||
if (arguments.isEmpty()) return null
|
||||
if (arguments.size == 1) {
|
||||
val argument = arguments.single()
|
||||
val expression = argument.getArgumentExpression()
|
||||
if (parameter.varargElementType != null && argument.getSpreadElement() == null) {
|
||||
return createVarargsHolder(arguments, this)
|
||||
}
|
||||
return KotlinConverter.convertOrEmpty(expression, this)
|
||||
}
|
||||
return createVarargsHolder(arguments, this)
|
||||
}
|
||||
|
||||
private fun createVarargsHolder(arguments: List<ValueArgument>, parent: UElement?): KotlinUExpressionList =
|
||||
KotlinUExpressionList(null, VARARGS, parent).apply {
|
||||
expressions = arguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), parent) }
|
||||
}
|
||||
|
||||
override val typeArgumentCount: Int
|
||||
get() = psi.typeArguments.size
|
||||
|
||||
@@ -139,4 +159,7 @@ class KotlinUFunctionCallExpression(
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("will be replaced by one from uast api when it comes")
|
||||
val VARARGS = UastSpecialExpressionKind("varargs")
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.uast.kotlin
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiType
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.uast.*
|
||||
|
||||
class KotlinUNamedExpression private constructor(
|
||||
override val name: String?,
|
||||
override val sourcePsi: PsiElement?,
|
||||
givenParent: UElement?,
|
||||
expressionProducer: (UElement) -> UExpression
|
||||
) : KotlinAbstractUElement(givenParent), UNamedExpression {
|
||||
|
||||
override val expression: UExpression by lz { expressionProducer(this) }
|
||||
|
||||
override val annotations: List<UAnnotation> = emptyList()
|
||||
|
||||
override val psi: PsiElement? = null
|
||||
|
||||
override val javaPsi: PsiElement? = null
|
||||
|
||||
companion object {
|
||||
internal fun create(name: String?, valueArgument: ValueArgument, uastParent: UElement?): UNamedExpression {
|
||||
val expression = valueArgument.getArgumentExpression()
|
||||
return KotlinUNamedExpression(name, valueArgument.asElement(), uastParent) { expressionParent ->
|
||||
expression?.let { expressionParent.getLanguagePlugin().convert<UExpression>(it, expressionParent) } ?: UastEmptyExpression
|
||||
}
|
||||
}
|
||||
|
||||
internal fun create(
|
||||
name: String?,
|
||||
valueArguments: List<ValueArgument>,
|
||||
uastParent: UElement?): UNamedExpression {
|
||||
return KotlinUNamedExpression(name, null, uastParent) { expressionParent ->
|
||||
KotlinUVarargExpression(valueArguments, expressionParent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
class KotlinUVarargExpression(private val valueArgs: List<ValueArgument>,
|
||||
uastParent: UElement?
|
||||
) : KotlinAbstractUExpression(uastParent), UCallExpressionEx {
|
||||
override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER
|
||||
|
||||
override val valueArguments: List<UExpression> by lz {
|
||||
valueArgs.map {
|
||||
it.getArgumentExpression()?.let { argumentExpression ->
|
||||
getLanguagePlugin().convert<UExpression>(argumentExpression, this)
|
||||
} ?: UastEmptyExpression
|
||||
}
|
||||
}
|
||||
|
||||
override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i)
|
||||
|
||||
override val valueArgumentCount: Int
|
||||
get() = valueArgs.size
|
||||
|
||||
override val psi: PsiElement?
|
||||
get() = null
|
||||
|
||||
override val methodIdentifier: UIdentifier?
|
||||
get() = null
|
||||
|
||||
override val classReference: UReferenceExpression?
|
||||
get() = null
|
||||
|
||||
override val methodName: String?
|
||||
get() = null
|
||||
|
||||
override val typeArgumentCount: Int
|
||||
get() = 0
|
||||
|
||||
override val typeArguments: List<PsiType>
|
||||
get() = emptyList()
|
||||
|
||||
override val returnType: PsiType?
|
||||
get() = null
|
||||
|
||||
override fun resolve() = null
|
||||
|
||||
override val receiver: UExpression?
|
||||
get() = null
|
||||
|
||||
override val receiverType: PsiType?
|
||||
get() = null
|
||||
}
|
||||
+3
-1
@@ -102,7 +102,7 @@ open class KotlinUSimpleReferenceExpression(
|
||||
private val resolvedCall: ResolvedCall<*>,
|
||||
private val accessorDescriptor: DeclarationDescriptor,
|
||||
val setterValue: KtExpression?
|
||||
) : UCallExpression, JvmDeclarationUElement {
|
||||
) : UCallExpressionEx, JvmDeclarationUElement {
|
||||
override val methodName: String?
|
||||
get() = accessorDescriptor.name.asString()
|
||||
|
||||
@@ -142,6 +142,8 @@ open class KotlinUSimpleReferenceExpression(
|
||||
emptyList()
|
||||
}
|
||||
|
||||
override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i)
|
||||
|
||||
override val typeArgumentCount: Int
|
||||
get() = resolvedCall.typeArguments.size
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
fun global(a: Int, b: Float) {}
|
||||
|
||||
fun withDefault(c: Int = 1, d: String = "aaa") {}
|
||||
|
||||
fun String.withReceiver(a: Int, b: Float) {}
|
||||
|
||||
fun call() {
|
||||
global(b = 2.2F, a = 2)
|
||||
withDefault(d = "bbb")
|
||||
"abc".withReceiver(1, 1.2F)
|
||||
Math.atan2(1.3, 3.4)
|
||||
unresolvedMethod("param1", "param2")
|
||||
java.lang.String.format("%i %i %i", 1, 2, 3)
|
||||
java.lang.String.format("%i %i %i", arrayOf(1, 2, 3))
|
||||
java.lang.String.format("%i %i %i", arrayOf(1, 2, 3), arrayOf(4, 5, 6))
|
||||
java.lang.String.format("%i %i %i", *"".chunked(2).toTypedArray())
|
||||
|
||||
with(A()) {
|
||||
"def".with2Receivers(8, 7.0F)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class A {
|
||||
|
||||
fun String.with2Receivers(a: Int, b: Float) {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
UFile (package = )
|
||||
UClass (name = ParametersDisorderKt)
|
||||
UAnnotationMethod (name = global)
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = b)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UAnnotationMethod (name = withDefault)
|
||||
UParameter (name = c)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
ULiteralExpression (value = 1)
|
||||
UParameter (name = d)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
ULiteralExpression (value = "aaa")
|
||||
UBlockExpression
|
||||
UAnnotationMethod (name = withReceiver)
|
||||
UParameter (name = $receiver)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = b)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UAnnotationMethod (name = call)
|
||||
UBlockExpression
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (global))
|
||||
USimpleNameReferenceExpression (identifier = global)
|
||||
ULiteralExpression (value = 2.2)
|
||||
ULiteralExpression (value = 2)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (withDefault))
|
||||
USimpleNameReferenceExpression (identifier = withDefault)
|
||||
ULiteralExpression (value = "bbb")
|
||||
UQualifiedReferenceExpression
|
||||
ULiteralExpression (value = "abc")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (withReceiver))
|
||||
USimpleNameReferenceExpression (identifier = withReceiver)
|
||||
ULiteralExpression (value = 1)
|
||||
ULiteralExpression (value = 1.2)
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = Math)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (atan2))
|
||||
USimpleNameReferenceExpression (identifier = atan2)
|
||||
ULiteralExpression (value = 1.3)
|
||||
ULiteralExpression (value = 3.4)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (unresolvedMethod))
|
||||
USimpleNameReferenceExpression (identifier = <anonymous class>)
|
||||
ULiteralExpression (value = "param1")
|
||||
ULiteralExpression (value = "param2")
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = java)
|
||||
USimpleNameReferenceExpression (identifier = lang)
|
||||
USimpleNameReferenceExpression (identifier = String)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 4))
|
||||
UIdentifier (Identifier (format))
|
||||
USimpleNameReferenceExpression (identifier = format)
|
||||
ULiteralExpression (value = "%i %i %i")
|
||||
ULiteralExpression (value = 1)
|
||||
ULiteralExpression (value = 2)
|
||||
ULiteralExpression (value = 3)
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = java)
|
||||
USimpleNameReferenceExpression (identifier = lang)
|
||||
USimpleNameReferenceExpression (identifier = String)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (format))
|
||||
USimpleNameReferenceExpression (identifier = format)
|
||||
ULiteralExpression (value = "%i %i %i")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 3))
|
||||
UIdentifier (Identifier (arrayOf))
|
||||
USimpleNameReferenceExpression (identifier = arrayOf)
|
||||
ULiteralExpression (value = 1)
|
||||
ULiteralExpression (value = 2)
|
||||
ULiteralExpression (value = 3)
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = java)
|
||||
USimpleNameReferenceExpression (identifier = lang)
|
||||
USimpleNameReferenceExpression (identifier = String)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 3))
|
||||
UIdentifier (Identifier (format))
|
||||
USimpleNameReferenceExpression (identifier = format)
|
||||
ULiteralExpression (value = "%i %i %i")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 3))
|
||||
UIdentifier (Identifier (arrayOf))
|
||||
USimpleNameReferenceExpression (identifier = arrayOf)
|
||||
ULiteralExpression (value = 1)
|
||||
ULiteralExpression (value = 2)
|
||||
ULiteralExpression (value = 3)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 3))
|
||||
UIdentifier (Identifier (arrayOf))
|
||||
USimpleNameReferenceExpression (identifier = arrayOf)
|
||||
ULiteralExpression (value = 4)
|
||||
ULiteralExpression (value = 5)
|
||||
ULiteralExpression (value = 6)
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
USimpleNameReferenceExpression (identifier = java)
|
||||
USimpleNameReferenceExpression (identifier = lang)
|
||||
USimpleNameReferenceExpression (identifier = String)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (format))
|
||||
USimpleNameReferenceExpression (identifier = format)
|
||||
ULiteralExpression (value = "%i %i %i")
|
||||
UQualifiedReferenceExpression
|
||||
UQualifiedReferenceExpression
|
||||
ULiteralExpression (value = "")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
|
||||
UIdentifier (Identifier (chunked))
|
||||
USimpleNameReferenceExpression (identifier = chunked)
|
||||
ULiteralExpression (value = 2)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0))
|
||||
UIdentifier (Identifier (toTypedArray))
|
||||
USimpleNameReferenceExpression (identifier = toTypedArray)
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (with))
|
||||
USimpleNameReferenceExpression (identifier = with)
|
||||
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0))
|
||||
UIdentifier (Identifier (A))
|
||||
USimpleNameReferenceExpression (identifier = <init>)
|
||||
ULambdaExpression
|
||||
UBlockExpression
|
||||
UQualifiedReferenceExpression
|
||||
ULiteralExpression (value = "def")
|
||||
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2))
|
||||
UIdentifier (Identifier (with2Receivers))
|
||||
USimpleNameReferenceExpression (identifier = with2Receivers)
|
||||
ULiteralExpression (value = 8)
|
||||
ULiteralExpression (value = 7.0)
|
||||
UClass (name = A)
|
||||
UAnnotationMethod (name = with2Receivers)
|
||||
UParameter (name = $receiver)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = a)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UParameter (name = b)
|
||||
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
|
||||
UBlockExpression
|
||||
UAnnotationMethod (name = A)
|
||||
@@ -0,0 +1,28 @@
|
||||
public final class ParametersDisorderKt {
|
||||
public static final fun global(@org.jetbrains.annotations.NotNull a: int, @org.jetbrains.annotations.NotNull b: float) : void {
|
||||
}
|
||||
public static final fun withDefault(@org.jetbrains.annotations.NotNull c: int, @org.jetbrains.annotations.NotNull d: java.lang.String) : void {
|
||||
}
|
||||
public static final fun withReceiver(@org.jetbrains.annotations.NotNull $receiver: java.lang.String, @org.jetbrains.annotations.NotNull a: int, @org.jetbrains.annotations.NotNull b: float) : void {
|
||||
}
|
||||
public static final fun call() : void {
|
||||
global(2.2, 2)
|
||||
withDefault("bbb")
|
||||
"abc".withReceiver(1, 1.2)
|
||||
Math.atan2(1.3, 3.4)
|
||||
<anonymous class>("param1", "param2")
|
||||
java.lang.String.format("%i %i %i", 1, 2, 3)
|
||||
java.lang.String.format("%i %i %i", arrayOf(1, 2, 3))
|
||||
java.lang.String.format("%i %i %i", arrayOf(1, 2, 3), arrayOf(4, 5, 6))
|
||||
java.lang.String.format("%i %i %i", "".chunked(2).toTypedArray())
|
||||
with(<init>(), {
|
||||
"def".with2Receivers(8, 7.0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public final class A {
|
||||
public final fun with2Receivers(@org.jetbrains.annotations.NotNull $receiver: java.lang.String, @org.jetbrains.annotations.NotNull a: int, @org.jetbrains.annotations.NotNull b: float) : void {
|
||||
}
|
||||
public fun A() = UastEmptyExpression
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package org.jetbrains.uast.test.kotlin
|
||||
|
||||
import com.intellij.psi.PsiAnnotation
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.asJava.toLightAnnotation
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.uast.*
|
||||
import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin
|
||||
import org.jetbrains.uast.test.env.findElementByText
|
||||
import org.jetbrains.uast.test.env.findElementByTextFromPsi
|
||||
import org.jetbrains.uast.visitor.AbstractUastVisitor
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
|
||||
class KotlinUastApiTest : AbstractKotlinUastTest() {
|
||||
override fun check(testName: String, file: UFile) {
|
||||
}
|
||||
|
||||
@Test fun testAnnotationParameters() {
|
||||
doTest("AnnotationParameters") { _, file ->
|
||||
val annotation = file.findElementByText<UAnnotation>("@IntRange(from = 10, to = 0)")
|
||||
assertEquals(annotation.findAttributeValue("from")?.evaluate(), 10)
|
||||
val toAttribute = annotation.findAttributeValue("to")!!
|
||||
assertEquals(toAttribute.evaluate(), 0)
|
||||
KtUsefulTestCase.assertInstanceOf(annotation.psi.toUElement(), UAnnotation::class.java)
|
||||
KtUsefulTestCase.assertInstanceOf(
|
||||
annotation.psi.cast<KtAnnotationEntry>().toLightAnnotation().toUElement(),
|
||||
UAnnotation::class.java
|
||||
)
|
||||
KtUsefulTestCase.assertInstanceOf(toAttribute.uastParent, UNamedExpression::class.java)
|
||||
KtUsefulTestCase.assertInstanceOf(toAttribute.psi.toUElement()?.uastParent, UNamedExpression::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testConvertStringTemplate() {
|
||||
doTest("StringTemplateInClass") { _, file ->
|
||||
val literalExpression = file.findElementByText<ULiteralExpression>("lorem")
|
||||
val psi = literalExpression.psi!!
|
||||
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
|
||||
val literalExpressionAgain = psi.toUElement()
|
||||
Assert.assertTrue(literalExpressionAgain is ULiteralExpression)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testConvertStringTemplateWithExpectedType() {
|
||||
doTest("StringTemplateWithVar") { _, file ->
|
||||
val index = file.psi.text.indexOf("foo")
|
||||
val stringTemplate = file.psi.findElementAt(index)!!.getParentOfType<KtStringTemplateExpression>(false)
|
||||
val uLiteral = stringTemplate.toUElementOfType<ULiteralExpression>()
|
||||
assertNull(uLiteral)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testNameContainingFile() {
|
||||
doTest("NameContainingFile") { _, file ->
|
||||
val foo = file.findElementByText<UClass>("class Foo")
|
||||
assertEquals(file.psi, foo.nameIdentifier!!.containingFile)
|
||||
|
||||
val bar = file.findElementByText<UMethod>("fun bar() {}")
|
||||
assertEquals(file.psi, bar.nameIdentifier!!.containingFile)
|
||||
|
||||
val xyzzy = file.findElementByText<UVariable>("val xyzzy: Int = 0")
|
||||
assertEquals(file.psi, xyzzy.nameIdentifier!!.containingFile)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testInterfaceMethodWithBody() {
|
||||
doTest("DefaultImpls") { _, file ->
|
||||
val bar = file.findElementByText<UMethod>("fun bar() = \"Hello!\"")
|
||||
assertFalse(bar.containingFile.text!!, bar.psi.modifierList.hasExplicitModifier(PsiModifier.DEFAULT))
|
||||
assertTrue(bar.containingFile.text!!, bar.psi.modifierList.hasModifierProperty(PsiModifier.DEFAULT))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testSAM() {
|
||||
doTest("SAM") { _, file ->
|
||||
assertNull(file.findElementByText<ULambdaExpression>("{ /* Not SAM */ }").functionalInterfaceType)
|
||||
|
||||
assertEquals("java.lang.Runnable",
|
||||
file.findElementByText<ULambdaExpression>("{/* Variable */}").functionalInterfaceType?.canonicalText)
|
||||
|
||||
assertEquals("java.lang.Runnable",
|
||||
file.findElementByText<ULambdaExpression>("{/* Assignment */}").functionalInterfaceType?.canonicalText)
|
||||
|
||||
assertEquals("java.lang.Runnable",
|
||||
file.findElementByText<ULambdaExpression>("{/* Type Cast */}").functionalInterfaceType?.canonicalText)
|
||||
|
||||
assertEquals("java.lang.Runnable",
|
||||
file.findElementByText<ULambdaExpression>("{/* Argument */}").functionalInterfaceType?.canonicalText)
|
||||
|
||||
assertEquals("java.lang.Runnable",
|
||||
file.findElementByText<ULambdaExpression>("{/* Return */}").functionalInterfaceType?.canonicalText)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testParameterPropertyWithAnnotation() {
|
||||
doTest("ParameterPropertyWithAnnotation") { _, file ->
|
||||
val test1 = file.classes.find { it.name == "Test1" }!!
|
||||
|
||||
val constructor1 = test1.methods.find { it.name == "Test1" }!!
|
||||
assertTrue(constructor1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" })
|
||||
|
||||
val getter1 = test1.methods.find { it.name == "getBar" }!!
|
||||
assertFalse(getter1.annotations.any { it.qualifiedName == "MyAnnotation" })
|
||||
|
||||
val setter1 = test1.methods.find { it.name == "setBar" }!!
|
||||
assertFalse(setter1.annotations.any { it.qualifiedName == "MyAnnotation" })
|
||||
assertFalse(setter1.uastParameters.first().annotations.any { it.qualifiedName == "MyAnnotation" })
|
||||
|
||||
|
||||
val test2 = file.classes.find { it.name == "Test2" }!!
|
||||
val constructor2 = test2.methods.find { it.name == "Test2" }!!
|
||||
assertFalse(constructor2.uastParameters.first().annotations.any { it.qualifiedName?.startsWith("MyAnnotation") ?: false })
|
||||
|
||||
val getter2 = test2.methods.find { it.name == "getBar" }!!
|
||||
getter2.annotations.single { it.qualifiedName == "MyAnnotation" }
|
||||
|
||||
val setter2 = test2.methods.find { it.name == "setBar" }!!
|
||||
setter2.annotations.single { it.qualifiedName == "MyAnnotation2" }
|
||||
setter2.uastParameters.first().annotations.single { it.qualifiedName == "MyAnnotation3" }
|
||||
|
||||
test2.fields.find { it.name == "bar" }!!.annotations.single { it.qualifiedName == "MyAnnotation5" }
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testConvertTypeInAnnotation() {
|
||||
doTest("TypeInAnnotation") { _, file ->
|
||||
val index = file.psi.text.indexOf("Test")
|
||||
val element = file.psi.findElementAt(index)!!.getParentOfType<KtUserType>(false)!!
|
||||
assertNotNull(element.getUastParentOfType(UAnnotation::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testElvisType() {
|
||||
doTest("ElvisType") { _, file ->
|
||||
val elvisExpression = file.findElementByText<UExpression>("text ?: return")
|
||||
assertEquals("String", elvisExpression.getExpressionType()!!.presentableText)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testFindAttributeDefaultValue() {
|
||||
doTest("AnnotationParameters") { _, file ->
|
||||
val witDefaultValue = file.findElementByText<UAnnotation>("@WithDefaultValue")
|
||||
assertEquals(42, witDefaultValue.findAttributeValue("value")!!.evaluate())
|
||||
assertEquals(42, witDefaultValue.findAttributeValue(null)!!.evaluate())
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testIfCondition() {
|
||||
doTest("IfStatement") { _, file ->
|
||||
val psiFile = file.psi
|
||||
val element = psiFile.findElementAt(psiFile.text.indexOf("\"abc\""))!!
|
||||
val binaryExpression = element.getParentOfType<KtBinaryExpression>(false)!!
|
||||
val uBinaryExpression = KotlinUastLanguagePlugin().convertElementWithParent(binaryExpression, null)!!
|
||||
UsefulTestCase.assertInstanceOf(uBinaryExpression.uastParent, UIfExpression::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWhenStringLiteral() {
|
||||
doTest("WhenStringLiteral") { _, file ->
|
||||
|
||||
file.findElementByTextFromPsi<ULiteralExpression>("abc").let { literalExpression ->
|
||||
val psi = literalExpression.psi!!
|
||||
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
|
||||
UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java)
|
||||
}
|
||||
|
||||
file.findElementByTextFromPsi<ULiteralExpression>("def").let { literalExpression ->
|
||||
val psi = literalExpression.psi!!
|
||||
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
|
||||
UsefulTestCase.assertInstanceOf(literalExpression.uastParent, USwitchClauseExpressionWithBody::class.java)
|
||||
}
|
||||
|
||||
file.findElementByTextFromPsi<ULiteralExpression>("def1").let { literalExpression ->
|
||||
val psi = literalExpression.psi!!
|
||||
Assert.assertTrue(psi is KtLiteralStringTemplateEntry)
|
||||
UsefulTestCase.assertInstanceOf(literalExpression.uastParent, UBlockExpression::class.java)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWhenAndDestructing() {
|
||||
doTest("WhenAndDestructing") { _, file ->
|
||||
|
||||
file.findElementByTextFromPsi<UExpression>("val (bindingContext, statementFilter) = arr").let { e ->
|
||||
val uBlockExpression = e.getParentOfType<UBlockExpression>()
|
||||
Assert.assertNotNull(uBlockExpression)
|
||||
val uMethod = uBlockExpression!!.getParentOfType<UMethod>()
|
||||
Assert.assertNotNull(uMethod)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBrokenMethodTypeResolve() {
|
||||
doTest("BrokenMethod") { _, file ->
|
||||
|
||||
file.accept(object : AbstractUastVisitor() {
|
||||
override fun visitCallExpression(node: UCallExpression): Boolean {
|
||||
node.returnType
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimpleAnnotated() {
|
||||
doTest("SimpleAnnotated") { _, file ->
|
||||
file.findElementByTextFromPsi<UField>("@SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field ->
|
||||
val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName }
|
||||
Assert.assertEquals(annotation.findDeclaredAttributeValue("version")?.evaluateString(), "1.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun UFile.checkUastSuperTypes(refText: String, superTypes: List<String>) {
|
||||
findElementByTextFromPsi<UClass>(refText, false).let {
|
||||
assertEquals("base classes", superTypes, it.uastSuperTypes.map { it.getQualifiedName() })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testSuperTypes() {
|
||||
doTest("SuperCalls") { _, file ->
|
||||
file.checkUastSuperTypes("B", listOf("A"))
|
||||
file.checkUastSuperTypes("O", listOf("A"))
|
||||
file.checkUastSuperTypes("innerObject ", listOf("A"))
|
||||
file.checkUastSuperTypes("InnerClass", listOf("A"))
|
||||
file.checkUastSuperTypes("object : A(\"textForAnon\")", listOf("A"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnonymousSuperTypes() {
|
||||
doTest("Anonymous") { _, file ->
|
||||
file.checkUastSuperTypes("object : Runnable { override fun run() {} }", listOf("java.lang.Runnable"))
|
||||
file.checkUastSuperTypes(
|
||||
"object : Runnable, Closeable { override fun close() {} override fun run() {} }",
|
||||
listOf("java.lang.Runnable", "java.io.Closeable")
|
||||
)
|
||||
file.checkUastSuperTypes(
|
||||
"object : InputStream(), Runnable { override fun read(): Int = 0; override fun run() {} }",
|
||||
listOf("java.io.InputStream", "java.lang.Runnable")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLiteralArraysTypes() {
|
||||
doTest("AnnotationParameters") { _, file ->
|
||||
file.findElementByTextFromPsi<UCallExpression>("intArrayOf(1, 2, 3)").let { field ->
|
||||
Assert.assertEquals("PsiType:int[]", field.returnType.toString())
|
||||
}
|
||||
file.findElementByTextFromPsi<UCallExpression>("[1, 2, 3]").let { field ->
|
||||
Assert.assertEquals("PsiType:int[]", field.returnType.toString())
|
||||
Assert.assertEquals("PsiType:int", field.typeArguments.single().toString())
|
||||
}
|
||||
file.findElementByTextFromPsi<UCallExpression>("[\"a\", \"b\", \"c\"]").let { field ->
|
||||
Assert.assertEquals("PsiType:String[]", field.returnType.toString())
|
||||
Assert.assertEquals("PsiType:String", field.typeArguments.single().toString())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTypeAliases() {
|
||||
doTest("TypeAliases") { _, file ->
|
||||
val g = (file.psi as KtFile).declarations.single { it.name == "G" } as KtTypeAlias
|
||||
val originalType = g.getTypeReference()!!.typeElement as KtFunctionType
|
||||
val originalTypeParameters = originalType.parameterList.toUElement() as UDeclarationsExpression
|
||||
Assert.assertTrue((originalTypeParameters.declarations.single() as UParameter).type.isValid)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file ->
|
||||
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation())")
|
||||
.findElementByTextFromPsi<UElement>("Annotation()")
|
||||
.sourcePsiElement
|
||||
.let { referenceExpression ->
|
||||
val convertedUAnnotation = referenceExpression
|
||||
.cast<KtReferenceExpression>()
|
||||
.toUElementOfType<UAnnotation>()
|
||||
?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})")
|
||||
|
||||
assertEquals("Annotation", convertedUAnnotation.qualifiedName)
|
||||
val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java)
|
||||
?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation")
|
||||
assertEquals("Annotation", lightAnnotation.qualifiedName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testParametersDisorder() = doTest("ParametersDisorder") { _, file ->
|
||||
|
||||
fun assertArguments(argumentsInPositionalOrder: List<String?>?, refText: String) =
|
||||
file.findElementByTextFromPsi<UCallExpression>(refText).let { call ->
|
||||
if (call !is UCallExpressionEx) throw AssertionError("${call.javaClass} is not a UCallExpressionEx")
|
||||
Assert.assertEquals(
|
||||
argumentsInPositionalOrder,
|
||||
call.resolve()?.let { psiMethod ->
|
||||
(0 until psiMethod.parameterList.parametersCount).map {
|
||||
call.getArgumentForParameter(it)?.asRenderString()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
assertArguments(listOf("2", "2.2"), "global(b = 2.2F, a = 2)")
|
||||
assertArguments(listOf(null, "\"bbb\""), "withDefault(d = \"bbb\")")
|
||||
assertArguments(listOf("1.3", "3.4"), "atan2(1.3, 3.4)")
|
||||
assertArguments(null, "unresolvedMethod(\"param1\", \"param2\")")
|
||||
assertArguments(listOf("\"%i %i %i\"", "varargs 1 : 2 : 3"), "format(\"%i %i %i\", 1, 2, 3)")
|
||||
assertArguments(listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3)"), "format(\"%i %i %i\", arrayOf(1, 2, 3))")
|
||||
assertArguments(
|
||||
listOf("\"%i %i %i\"", "varargs arrayOf(1, 2, 3) : arrayOf(4, 5, 6)"),
|
||||
"format(\"%i %i %i\", arrayOf(1, 2, 3), arrayOf(4, 5, 6))"
|
||||
)
|
||||
assertArguments(listOf("\"%i %i %i\"", "\"\".chunked(2).toTypedArray()"), "format(\"%i %i %i\", *\"\".chunked(2).toTypedArray())")
|
||||
assertArguments(listOf("\"def\"", "8", "7.0"), "with2Receivers(8, 7.0F)")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun <T, R> Iterable<T>.assertedFind(value: R, transform: (T) -> R): T =
|
||||
find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}")
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.jetbrains.uast.test.kotlin
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() {
|
||||
@Test fun testLocalDeclarations() = doTest("LocalDeclarations")
|
||||
|
||||
@Test fun testSimple() = doTest("Simple")
|
||||
|
||||
@Test fun testWhenIs() = doTest("WhenIs")
|
||||
|
||||
@Test fun testDefaultImpls() = doTest("DefaultImpls")
|
||||
|
||||
@Test fun testElvis() = doTest("Elvis")
|
||||
|
||||
@Test fun testPropertyAccessors() = doTest("PropertyAccessors")
|
||||
|
||||
@Test fun testPropertyInitializer() = doTest("PropertyInitializer")
|
||||
|
||||
@Test fun testPropertyInitializerWithoutSetter() = doTest("PropertyInitializerWithoutSetter")
|
||||
|
||||
@Test fun testAnnotationParameters() = doTest("AnnotationParameters")
|
||||
|
||||
@Test fun testEnumValueMembers() = doTest("EnumValueMembers")
|
||||
|
||||
@Test fun testStringTemplate() = doTest("StringTemplate")
|
||||
|
||||
@Test fun testStringTemplateComplex() = doTest("StringTemplateComplex")
|
||||
|
||||
@Test fun testQualifiedConstructorCall() = doTest("QualifiedConstructorCall")
|
||||
|
||||
@Test fun testPropertyDelegate() = doTest("PropertyDelegate") { testName, file -> check(testName, file, false) }
|
||||
|
||||
@Test fun testLocalVariableWithAnnotation() = doTest("LocalVariableWithAnnotation")
|
||||
|
||||
@Test fun testPropertyWithAnnotation() = doTest("PropertyWithAnnotation")
|
||||
|
||||
@Test fun testIfStatement() = doTest("IfStatement")
|
||||
|
||||
@Test fun testInnerClasses() = doTest("InnerClasses")
|
||||
|
||||
@Test fun testSimpleScript() = doTest("SimpleScript") { testName, file -> check(testName, file, false) }
|
||||
|
||||
@Test fun testDestructuringDeclaration() = doTest("DestructuringDeclaration")
|
||||
|
||||
@Test fun testDefaultParameterValues() = doTest("DefaultParameterValues")
|
||||
|
||||
@Test fun testParameterPropertyWithAnnotation() = doTest("ParameterPropertyWithAnnotation")
|
||||
|
||||
@Test fun testParametersWithDefaultValues() = doTest("ParametersWithDefaultValues")
|
||||
|
||||
@Test
|
||||
fun testUnexpectedContainer() = doTest("UnexpectedContainerException")
|
||||
|
||||
@Test
|
||||
fun testWhenStringLiteral() = doTest("WhenStringLiteral")
|
||||
|
||||
@Test
|
||||
fun testWhenAndDestructing() = doTest("WhenAndDestructing") { testName, file -> check(testName, file, false) }
|
||||
|
||||
@Test
|
||||
fun testSuperCalls() = doTest("SuperCalls")
|
||||
|
||||
@Test
|
||||
fun testConstructors() = doTest("Constructors")
|
||||
|
||||
@Test
|
||||
fun testClassAnnotation() = doTest("ClassAnnotation")
|
||||
|
||||
@Test
|
||||
fun testReceiverFun() = doTest("ReceiverFun")
|
||||
|
||||
@Test
|
||||
fun testAnonymous() = doTest("Anonymous")
|
||||
|
||||
@Test
|
||||
fun testAnnotationComplex() = doTest("AnnotationComplex")
|
||||
|
||||
@Test
|
||||
fun testParametersDisorder() = doTest("ParametersDisorder") { testName, file ->
|
||||
// disabled due to inconsistent parents for 2-receivers call (KT-22344)
|
||||
check(testName, file, false)
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.uast.test.env
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.UFile
|
||||
import org.jetbrains.uast.toUElementOfType
|
||||
import org.jetbrains.uast.visitor.UastVisitor
|
||||
import java.io.File
|
||||
import kotlin.test.fail
|
||||
|
||||
abstract class AbstractUastTest : AbstractTestWithCoreEnvironment() {
|
||||
protected companion object {
|
||||
val TEST_DATA_DIR = File("testData")
|
||||
}
|
||||
|
||||
abstract fun getVirtualFile(testName: String): VirtualFile
|
||||
abstract fun check(testName: String, file: UFile)
|
||||
|
||||
fun doTest(testName: String, checkCallback: (String, UFile) -> Unit = { testName, file -> check(testName, file) }) {
|
||||
val virtualFile = getVirtualFile(testName)
|
||||
|
||||
val psiFile = psiManager.findFile(virtualFile) ?: error("Can't get psi file for $testName")
|
||||
val uFile = uastContext.convertElementWithParent(psiFile, null) ?: error("Can't get UFile for $testName")
|
||||
checkCallback(testName, uFile as UFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> UElement.findElementByText(refText: String, cls: Class<T>): T {
|
||||
val matchingElements = mutableListOf<T>()
|
||||
accept(object : UastVisitor {
|
||||
override fun visitElement(node: UElement): Boolean {
|
||||
if (cls.isInstance(node) && node.psi?.text == refText) {
|
||||
matchingElements.add(node as T)
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
if (matchingElements.isEmpty()) {
|
||||
throw IllegalArgumentException("Reference '$refText' not found")
|
||||
}
|
||||
if (matchingElements.size != 1) {
|
||||
throw IllegalArgumentException("Reference '$refText' is ambiguous")
|
||||
}
|
||||
return matchingElements.single()
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> UElement.findElementByText(refText: String): T = findElementByText(refText, T::class.java)
|
||||
|
||||
inline fun <reified T : UElement> UElement.findElementByTextFromPsi(refText: String, strict: Boolean = true): T =
|
||||
(this.psi ?: fail("no psi for $this")).findUElementByTextFromPsi(refText, strict)
|
||||
|
||||
inline fun <reified T : UElement> PsiElement.findUElementByTextFromPsi(refText: String, strict: Boolean = true): T {
|
||||
val elementAtStart = this.findElementAt(this.text.indexOf(refText))
|
||||
?: throw AssertionError("requested text '$refText' was not found in $this")
|
||||
val uElementContainingText = elementAtStart.parentsWithSelf.let {
|
||||
if (strict) it.dropWhile { !it.text.contains(refText) } else it
|
||||
}.mapNotNull { it.toUElementOfType<T>() }.firstOrNull()
|
||||
?: throw AssertionError("requested text '$refText' not found as '${T::class.java.canonicalName}' in $this")
|
||||
if (strict && uElementContainingText.psi != null && uElementContainingText.psi?.text != refText) {
|
||||
throw AssertionError("requested text '$refText' found as '${uElementContainingText.psi?.text}' in $uElementContainingText")
|
||||
}
|
||||
return uElementContainingText;
|
||||
}
|
||||
Reference in New Issue
Block a user