191: Uast: implementation of UReferenceExpression.referenceNameElement (KT-29025)

This commit is contained in:
Nicolay Mitropolsky
2019-01-25 11:13:43 +03:00
parent a3ea7bcd3b
commit 795f9d83e1
8 changed files with 386 additions and 0 deletions
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2016 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.PsiNamedElement
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.UastQualifiedExpressionAccessType
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUQualifiedReferenceExpression(
override val psi: KtDotQualifiedExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UQualifiedReferenceExpression, DelegatedMultiResolve,
KotlinUElementWithType, KotlinEvaluatableUElement {
override val receiver by lz { KotlinConverter.convertOrEmpty(psi.receiverExpression, this) }
override val selector by lz { KotlinConverter.convertOrEmpty(psi.selectorExpression, this) }
override val accessType = UastQualifiedExpressionAccessType.SIMPLE
override fun resolve() = psi.selectorExpression?.resolveCallToDeclaration(this)
override val resolvedName: String?
get() = (resolve() as? PsiNamedElement)?.name
override val referenceNameElement: UElement?
get() = when (val selector = selector) {
is UCallExpression -> selector.methodIdentifier
else -> super.referenceNameElement
}
}
@@ -0,0 +1,226 @@
/*
* Copyright 2010-2016 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.PsiMethod
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.utils.addToStdlib.constant
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
import org.jetbrains.uast.visitor.UastVisitor
open class KotlinUSimpleReferenceExpression(
override val psi: KtSimpleNameExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression, KotlinUElementWithType, KotlinEvaluatableUElement {
private val resolvedDeclaration by lz { psi.resolveCallToDeclaration(this) }
override val identifier get() = psi.getReferencedName()
override fun resolve() = resolvedDeclaration
override val resolvedName: String?
get() = (resolvedDeclaration as? PsiNamedElement)?.name
override fun accept(visitor: UastVisitor) {
visitor.visitSimpleNameReferenceExpression(this)
if (psi.parent.destructuringDeclarationInitializer != true) {
visitAccessorCalls(visitor)
}
visitor.afterVisitSimpleNameReferenceExpression(this)
}
override val referenceNameElement: UElement? by lz { psi.getIdentifier()?.toUElement() }
private fun visitAccessorCalls(visitor: UastVisitor) {
// Visit Kotlin get-set synthetic Java property calls as function calls
val bindingContext = psi.analyze()
val access = psi.readWriteAccess()
val resolvedCall = psi.getResolvedCall(bindingContext)
val resultingDescriptor = resolvedCall?.resultingDescriptor as? SyntheticJavaPropertyDescriptor
if (resultingDescriptor != null) {
val setterValue = if (access.isWrite) {
findAssignment(psi, psi.parent)?.right ?: run {
visitor.afterVisitSimpleNameReferenceExpression(this)
return
}
} else {
null
}
if (resolvedCall != null) {
if (access.isRead) {
val getDescriptor = resultingDescriptor.getMethod
KotlinAccessorCallExpression(psi, this, resolvedCall, getDescriptor, null).accept(visitor)
}
if (access.isWrite && setterValue != null) {
val setDescriptor = resultingDescriptor.setMethod
if (setDescriptor != null) {
KotlinAccessorCallExpression(psi, this, resolvedCall, setDescriptor, setterValue).accept(visitor)
}
}
}
}
}
private tailrec fun findAssignment(prev: PsiElement?, element: PsiElement?): KtBinaryExpression? = when (element) {
is KtBinaryExpression -> if (element.left == prev && element.operationToken == KtTokens.EQ) element else null
is KtQualifiedExpression -> findAssignment(element, element.parent)
is KtSimpleNameExpression -> findAssignment(element, element.parent)
else -> null
}
class KotlinAccessorCallExpression(
override val psi: KtSimpleNameExpression,
override val uastParent: KotlinUSimpleReferenceExpression,
private val resolvedCall: ResolvedCall<*>,
private val accessorDescriptor: DeclarationDescriptor,
val setterValue: KtExpression?
) : UCallExpressionEx, DelegatedMultiResolve, JvmDeclarationUElementPlaceholder {
override val methodName: String?
get() = accessorDescriptor.name.asString()
override val receiver: UExpression?
get() {
val containingElement = uastParent.uastParent
return if (containingElement is UQualifiedReferenceExpression && containingElement.selector == this)
containingElement.receiver
else
null
}
override val javaPsi: PsiElement? = null
override val sourcePsi: PsiElement? = psi
override val annotations: List<UAnnotation>
get() = emptyList()
override val receiverType by lz {
val type = (resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver)?.type ?: return@lz null
type.toPsiType(this, psi, boxed = true)
}
override val methodIdentifier: UIdentifier? by lazy { KotlinUIdentifier(psi.getReferencedNameElement(), this) }
override val classReference: UReferenceExpression?
get() = null
override val valueArgumentCount: Int
get() = if (setterValue != null) 1 else 0
override val valueArguments by lz {
if (setterValue != null)
listOf(KotlinConverter.convertOrEmpty(setterValue, this))
else
emptyList()
}
override fun getArgumentForParameter(i: Int): UExpression? = valueArguments.getOrNull(i)
override val typeArgumentCount: Int
get() = resolvedCall.typeArguments.size
override val typeArguments by lz {
resolvedCall.typeArguments.values.map { it.toPsiType(this, psi, true) }
}
override val returnType by lz {
(accessorDescriptor as? CallableDescriptor)?.returnType?.toPsiType(this, psi, boxed = false)
}
override val kind: UastCallKind
get() = UastCallKind.METHOD_CALL
override fun resolve(): PsiMethod? {
val source = accessorDescriptor.toSource()
return resolveSource(psi, accessorDescriptor, source)
}
}
enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) {
READ(true, false), WRITE(false, true), READ_WRITE(true, true)
}
private fun KtExpression.readWriteAccess(): ReferenceAccess {
var expression = getQualifiedExpressionForSelectorOrThis()
loop@ while (true) {
val parent = expression.parent
when (parent) {
is KtParenthesizedExpression, is KtAnnotatedExpression, is KtLabeledExpression -> expression = parent as KtExpression
else -> break@loop
}
}
val assignment = expression.getAssignmentByLHS()
if (assignment != null) {
return when (assignment.operationToken) {
KtTokens.EQ -> ReferenceAccess.WRITE
else -> ReferenceAccess.READ_WRITE
}
}
return if ((expression.parent as? KtUnaryExpression)?.operationToken
in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) })
ReferenceAccess.READ_WRITE
else
ReferenceAccess.READ
}
}
class KotlinClassViaConstructorUSimpleReferenceExpression(
override val psi: KtCallElement,
override val identifier: String,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression, KotlinUElementWithType {
override val resolvedName: String?
get() = (psi.getResolvedCall(psi.analyze())?.resultingDescriptor as? ConstructorDescriptor)
?.containingDeclaration?.name?.asString()
override fun resolve(): PsiElement? {
val resolvedCall = psi.getResolvedCall(psi.analyze())
val resultingDescriptor = resolvedCall?.resultingDescriptor as? ConstructorDescriptor ?: return null
val clazz = resultingDescriptor.containingDeclaration
return clazz.toSource()?.getMaybeLightElement(this)
}
}
class KotlinStringUSimpleReferenceExpression(
override val identifier: String,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression {
override val psi: PsiElement?
get() = null
override fun resolve() = null
override val resolvedName: String?
get() = identifier
}
+24
View File
@@ -0,0 +1,24 @@
Closeable -> USimpleNameReferenceExpression (identifier = Closeable) from KtDotQualifiedExpression
io -> USimpleNameReferenceExpression (identifier = io) from KtDotQualifiedExpression
java -> USimpleNameReferenceExpression (identifier = java) from KtNameReferenceExpression
io -> USimpleNameReferenceExpression (identifier = io) from KtNameReferenceExpression
Closeable -> USimpleNameReferenceExpression (identifier = Closeable) from KtNameReferenceExpression
InputStream -> USimpleNameReferenceExpression (identifier = InputStream) from KtDotQualifiedExpression
io -> USimpleNameReferenceExpression (identifier = io) from KtDotQualifiedExpression
java -> USimpleNameReferenceExpression (identifier = java) from KtNameReferenceExpression
io -> USimpleNameReferenceExpression (identifier = io) from KtNameReferenceExpression
InputStream -> USimpleNameReferenceExpression (identifier = InputStream) from KtNameReferenceExpression
Runnable -> USimpleNameReferenceExpression (identifier = Runnable) from KtNameReferenceExpression
run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
runnable -> USimpleNameReferenceExpression (identifier = runnable) from KtNameReferenceExpression
run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
Runnable -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
runnable2 -> USimpleNameReferenceExpression (identifier = runnable2) from KtNameReferenceExpression
run -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
Runnable -> USimpleNameReferenceExpression (identifier = Runnable) from KtNameReferenceExpression
Closeable -> USimpleNameReferenceExpression (identifier = Closeable) from KtNameReferenceExpression
InputStream -> UObjectLiteralExpression from KtNameReferenceExpression
Runnable -> USimpleNameReferenceExpression (identifier = Runnable) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
@@ -0,0 +1,6 @@
Test -> UAnnotation (fqName = null) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
MyAnnotation -> UAnnotation (fqName = MyAnnotation) from KtNameReferenceExpression
MyAnnotation -> UAnnotation (fqName = MyAnnotation) from KtNameReferenceExpression
MyAnnotation -> UAnnotation (fqName = MyAnnotation) from KtNameReferenceExpression
MyAnnotation -> UAnnotation (fqName = MyAnnotation) from KtNameReferenceExpression
@@ -0,0 +1,44 @@
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
i -> USimpleNameReferenceExpression (identifier = i) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
i -> USimpleNameReferenceExpression (identifier = i) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
i -> USimpleNameReferenceExpression (identifier = i) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
a -> USimpleNameReferenceExpression (identifier = a) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
i -> USimpleNameReferenceExpression (identifier = i) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
a -> USimpleNameReferenceExpression (identifier = a) from KtNameReferenceExpression
s -> USimpleNameReferenceExpression (identifier = s) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
a -> USimpleNameReferenceExpression (identifier = a) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
i -> USimpleNameReferenceExpression (identifier = i) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
a -> USimpleNameReferenceExpression (identifier = a) from KtNameReferenceExpression
s -> USimpleNameReferenceExpression (identifier = s) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
s -> USimpleNameReferenceExpression (identifier = s) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtDotQualifiedExpression
local -> USimpleNameReferenceExpression (identifier = local) from KtNameReferenceExpression
toString -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
@@ -0,0 +1,7 @@
Boolean -> USimpleNameReferenceExpression (identifier = Boolean) from KtNameReferenceExpression
Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) from KtNameReferenceExpression
Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) from KtNameReferenceExpression
Int -> USimpleNameReferenceExpression (identifier = Int) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
bar -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) from KtNameReferenceExpression
Local -> UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) from KtNameReferenceExpression
@@ -0,0 +1,5 @@
Suppress -> UAnnotation (fqName = kotlin.Suppress) from KtNameReferenceExpression
println -> UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1)) from KtNameReferenceExpression
kotlin -> USimpleNameReferenceExpression (identifier = kotlin) from KtNameReferenceExpression
SinceKotlin -> UAnnotation (fqName = kotlin.SinceKotlin) from KtNameReferenceExpression
String -> USimpleNameReferenceExpression (identifier = String) from KtNameReferenceExpression
@@ -0,0 +1,28 @@
package org.jetbrains.uast.test.kotlin
import com.intellij.psi.PsiElement
import org.jetbrains.uast.UFile
import org.jetbrains.uast.UReferenceExpression
import org.jetbrains.uast.test.common.UElementToParentMap
import org.jetbrains.uast.test.common.kotlin.IdentifiersTestBase
import org.jetbrains.uast.test.env.assertEqualsToFile
import org.jetbrains.uast.toUElementOfType
import java.io.File
abstract class AbstractKotlinIdentifiersTest : AbstractKotlinUastTest(), IdentifiersTestBase {
private fun getTestFile(testName: String, ext: String) =
File(File(AbstractKotlinUastTest.TEST_KOTLIN_MODEL_DIR, testName).canonicalPath + '.' + ext)
override fun getIdentifiersFile(testName: String): File = getTestFile(testName, "identifiers.txt")
override fun check(testName: String, file: UFile) {
super.check(testName, file)
assertEqualsToFile("refNames", getTestFile(testName, "refNames.txt"), file.asRefNames())
}
}
fun UFile.asRefNames() = object : UElementToParentMap({ it.toUElementOfType<UReferenceExpression>()?.referenceNameElement }) {
override fun renderSource(element: PsiElement): String = element.javaClass.simpleName
}.visitUFileAndGetResult(this)