183: Uast: multiresolve (KT-27244)

This commit is contained in:
Nicolay Mitropolsky
2018-11-07 20:11:48 +03:00
parent 2e83ec1551
commit 0ee98a8d1d
24 changed files with 1697 additions and 31 deletions
+6
View File
@@ -142,6 +142,12 @@
description="Whether to use an experimental implementation of Kotlin-as-Java classes"
defaultValue="false"
restartRequired="false"/>
<registryKey key="kotlin.uast.multiresolve.enabled"
description="Whether to use muiti resolve for UAST in Kotlin provided by `Call.resolveCandidates`,
otherwise PsiPolyVariantReference-based multiResolve will be performed"
defaultValue="false"
restartRequired="false"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.uast">
@@ -0,0 +1,65 @@
/*
* 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.internal
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.resolveCandidates
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
class IdeaKotlinUastResolveProviderService : KotlinUastResolveProviderService {
override fun getBindingContext(element: KtElement) = element.analyze(BodyResolveMode.PARTIAL)
override fun getTypeMapper(element: KtElement): KotlinTypeMapper? {
return KotlinTypeMapper(
getBindingContext(element), ClassBuilderMode.LIGHT_CLASSES,
IncompatibleClassTracker.DoNothing, JvmAbi.DEFAULT_MODULE_NAME, JvmTarget.DEFAULT, KotlinTypeMapper.RELEASE_COROUTINES_DEFAULT,
false
)
}
override fun isJvmElement(psiElement: PsiElement): Boolean {
val module = psiElement.module
return module == null || TargetPlatformDetector.getPlatform(module) is JvmPlatform
}
override fun getLanguageVersionSettings(element: KtElement): LanguageVersionSettings {
return element.languageVersionSettings
}
override fun getReferenceVariants(ktElement: KtElement, nameHint: String): Sequence<DeclarationDescriptor> {
val resolutionFacade = ktElement.getResolutionFacade()
val bindingContext = ktElement.analyze()
val call = ktElement.getCall(bindingContext) ?: return emptySequence()
return call.resolveCandidates(bindingContext, resolutionFacade).map { it.candidateDescriptor }.asSequence()
}
}
@@ -0,0 +1,557 @@
/*
* Copyright 2010-2015 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.lang.Language
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
import org.jetbrains.uast.kotlin.expressions.*
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable
interface KotlinUastResolveProviderService {
fun getBindingContext(element: KtElement): BindingContext
fun getTypeMapper(element: KtElement): KotlinTypeMapper?
fun getLanguageVersionSettings(element: KtElement): LanguageVersionSettings
fun isJvmElement(psiElement: PsiElement): Boolean
fun getReferenceVariants(ktElement: KtElement, nameHint: String): Sequence<DeclarationDescriptor>
}
var PsiElement.destructuringDeclarationInitializer: Boolean? by UserDataProperty(Key.create("kotlin.uast.destructuringDeclarationInitializer"))
class KotlinUastLanguagePlugin : UastLanguagePlugin {
override val priority = 10
override val language: Language
get() = KotlinLanguage.INSTANCE
override fun isFileSupported(fileName: String): Boolean {
return fileName.endsWith(".kt", false) || fileName.endsWith(".kts", false)
}
private val PsiElement.isJvmElement: Boolean
get() {
val resolveProvider = ServiceManager.getService(project, KotlinUastResolveProviderService::class.java)
return resolveProvider.isJvmElement(this)
}
override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>?): UElement? {
if (!element.isJvmElement) return null
return convertDeclarationOrElement(element, parent, requiredType)
}
override fun convertElementWithParent(element: PsiElement, requiredType: Class<out UElement>?): UElement? {
if (!element.isJvmElement) return null
if (element is PsiFile) return convertDeclaration(element, null, requiredType)
if (element is KtLightClassForFacade) return convertDeclaration(element, null, requiredType)
return convertDeclarationOrElement(element, null, requiredType)
}
private fun convertDeclarationOrElement(element: PsiElement, givenParent: UElement?, requiredType: Class<out UElement>?): UElement? {
if (element is UElement) return element
if (element.isValid) {
element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement ->
return if (requiredType == null || requiredType.isInstance(cachedUElement)) cachedUElement else null
}
}
val uElement = convertDeclaration(element, givenParent, requiredType)
?: KotlinConverter.convertPsiElement(element, givenParent, requiredType)
/*
if (uElement != null) {
element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, WeakReference(uElement))
}
*/
return uElement
}
override fun getMethodCallExpression(
element: PsiElement,
containingClassFqName: String?,
methodName: String
): UastLanguagePlugin.ResolvedMethod? {
if (element !is KtCallExpression) return null
val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.name.asString() != methodName) return null
val parent = element.parent
val parentUElement = convertElementWithParent(parent, null) ?: return null
val uExpression = KotlinUFunctionCallExpression(element, parentUElement, resolvedCall)
val method = uExpression.resolve() ?: return null
if (method.name != methodName) return null
return UastLanguagePlugin.ResolvedMethod(uExpression, method)
}
override fun getConstructorCallExpression(
element: PsiElement,
fqName: String
): UastLanguagePlugin.ResolvedConstructor? {
if (element !is KtCallExpression) return null
val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor !is ConstructorDescriptor
|| resultingDescriptor.returnType.constructor.declarationDescriptor?.name?.asString() != fqName) {
return null
}
val parent = KotlinConverter.unwrapElements(element.parent) ?: return null
val parentUElement = convertElementWithParent(parent, null) ?: return null
val uExpression = KotlinUFunctionCallExpression(element, parentUElement, resolvedCall)
val method = uExpression.resolve() ?: return null
val containingClass = method.containingClass ?: return null
return UastLanguagePlugin.ResolvedConstructor(uExpression, method, containingClass)
}
internal fun convertDeclaration(element: PsiElement,
givenParent: UElement?,
requiredType: Class<out UElement>?): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) }
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
fun <P : PsiElement, K : KtElement> buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
val original = element.originalElement
return with(requiredType) {
when (original) {
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
is KtLightClass -> when (original.kotlinOrigin) {
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent)
}
else -> el<UClass> { KotlinUClass.create(original, givenParent) }
}
is KtLightFieldImpl.KtLightEnumConstant -> el<UEnumConstant>(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant))
is KtLightField -> el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiVariable -> el<UVariable>(buildKt(original.ktElement, ::KotlinUVariable))
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original, givenParent)
}
is KtClassOrObject -> el<UClass> {
original.toLightClass()?.let { lightClass ->
KotlinUClass.create(lightClass, givenParent)
}
}
is KtFunction ->
if (original.isLocal) {
el<ULambdaExpression> {
val parent = original.parent
if (parent is KtLambdaExpression) {
KotlinULambdaExpression(parent, givenParent) // your parent is the ULambdaExpression
} else if (original.name.isNullOrEmpty()) {
createLocalFunctionLambdaExpression(original, givenParent)
}
else {
val uDeclarationsExpression = createLocalFunctionDeclaration(original, givenParent)
val localFunctionVar = uDeclarationsExpression.declarations.single() as KotlinLocalFunctionUVariable
localFunctionVar.uastInitializer
}
}
}
else {
el<UMethod> {
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, requiredType)
}
}
is KtPropertyAccessor -> el<UMethod> {
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, requiredType)
}
is KtProperty ->
if (original.isLocal) {
KotlinConverter.convertPsiElement(element, givenParent, requiredType)
}
else {
convertNonLocalProperty(original, givenParent, requiredType)
}
is KtParameter -> el<UParameter> {
val ownerFunction = original.ownerFunction as? KtFunction ?: return null
val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return null
val lightParameter = lightMethod.parameterList.parameters.find { it.name == original.name } ?: return null
KotlinUParameter(lightParameter, original, givenParent)
}
is KtFile -> el<UFile> { KotlinUFile(original, this@KotlinUastLanguagePlugin) }
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) }
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
is KtCallExpression ->
if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) {
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
} else null
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType)
else -> null
}
}
}
private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightFieldImpl.KtLightEnumConstant) {
KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent)
}
else {
null
}
}
}
override fun isExpressionValueUsed(element: UExpression): Boolean {
return when (element) {
is KotlinUSimpleReferenceExpression.KotlinAccessorCallExpression -> element.setterValue != null
is KotlinAbstractUExpression -> {
val ktElement = element.psi as? KtElement ?: return false
ktElement.analyze()[BindingContext.USED_AS_EXPRESSION, ktElement] ?: false
}
else -> false
}
}
}
internal inline fun <reified ActualT : UElement> Class<out UElement>?.el(f: () -> UElement?): UElement? {
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
}
internal inline fun <reified ActualT : UElement> Class<out UElement>?.expr(f: () -> UExpression?): UExpression? {
return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null
}
private fun convertNonLocalProperty(property: KtProperty,
givenParent: UElement?,
requiredType: Class<out UElement>?): UElement? {
val methods = LightClassUtil.getLightClassPropertyMethods(property)
return methods.backingField?.let { backingField ->
with(requiredType) {
el<UField> { KotlinUField(backingField, (backingField as? KtLightElement<*,*>)?.kotlinOrigin, givenParent) }
}
} ?: methods.getter?.let { getter ->
KotlinUastLanguagePlugin().convertDeclaration(getter, givenParent, requiredType)
}
}
internal object KotlinConverter {
internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) {
is KtValueArgumentList -> unwrapElements(element.parent)
is KtValueArgument -> unwrapElements(element.parent)
is KtDeclarationModifierList -> unwrapElements(element.parent)
is KtContainerNode -> unwrapElements(element.parent)
is KtSimpleNameStringTemplateEntry -> unwrapElements(element.parent)
is KtLightParameterList -> unwrapElements(element.parent)
is KtTypeElement -> unwrapElements(element.parent)
else -> element
}
private val identifiersTokens =
setOf(KtTokens.IDENTIFIER, KtTokens.CONSTRUCTOR_KEYWORD, KtTokens.THIS_KEYWORD, KtTokens.SUPER_KEYWORD, KtTokens.OBJECT_KEYWORD)
internal fun convertPsiElement(element: PsiElement?,
givenParent: UElement?,
requiredType: Class<out UElement>?): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
return { ctor(element as P, givenParent) }
}
return with (requiredType) { when (element) {
is KtParameterList -> el<UDeclarationsExpression> {
val declarationsExpression = KotlinUDeclarationsExpression(givenParent)
declarationsExpression.apply {
declarations = element.parameters.mapIndexed { i, p ->
KotlinUParameter(UastKotlinPsiParameter.create(p, element, declarationsExpression, i), p, this)
}
}
}
is KtClassBody -> el<UExpressionList>(build(KotlinUExpressionList.Companion::createClassBody))
is KtCatchClause -> el<UCatchClause>(build(::KotlinUCatchClause))
is KtVariableDeclaration ->
if (element is KtProperty && !element.isLocal) {
el<UField> {
LightClassUtil.getLightClassBackingField(element)?.let {
KotlinUField(it, element, givenParent)
}
}
}
else {
el<UVariable> {
convertVariablesDeclaration(element, givenParent).declarations.singleOrNull()
}
}
is KtExpression -> KotlinConverter.convertExpression(element, givenParent, requiredType)
is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) }
is KtLightElementBase -> {
val expression = element.kotlinOrigin
when (expression) {
is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, requiredType)
else -> el<UExpression> { UastEmptyExpression(givenParent) }
}
}
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el<ULiteralExpression>(build(::KotlinStringULiteralExpression))
is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, requiredType) } ?: expr<UExpression> { UastEmptyExpression }
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
is KtWhenCondition -> convertWhenCondition(element, givenParent, requiredType)
is KtTypeReference -> el<UTypeReferenceExpression> { LazyKotlinUTypeReferenceExpression(element, givenParent) }
is KtConstructorDelegationCall ->
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
is KtSuperTypeCallEntry ->
el<UExpression> {
(element.getParentOfType<KtClassOrObject>(true)?.parent as? KtObjectLiteralExpression)
?.toUElementOfType<UExpression>()
?: KotlinUFunctionCallExpression(element, givenParent)
}
is KtImportDirective -> el<UImportStatement>(build(::KotlinUImportStatement))
else -> {
if (element is LeafPsiElement) {
if (element.elementType in identifiersTokens)
if (element.elementType != KtTokens.OBJECT_KEYWORD || element.getParentOfType<KtObjectDeclaration>(false)?.nameIdentifier == null)
el<UIdentifier>(build(::KotlinUIdentifier))
else null
else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression)
el<UIdentifier>(build(::KotlinUIdentifier))
else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression)
el<UIdentifier> {
UIdentifier(
element,
KotlinUCollectionLiteralExpression(
element.parent as KtCollectionLiteralExpression,
null
)
)
}
else null
} else null
}
}}
}
internal fun convertEntry(entry: KtStringTemplateEntry,
givenParent: UElement?,
requiredType: Class<out UElement>? = null): UExpression? {
return with(requiredType) {
if (entry is KtStringTemplateEntryWithExpression) {
expr<UExpression> {
KotlinConverter.convertOrEmpty(entry.expression, givenParent)
}
}
else {
expr<ULiteralExpression> {
if (entry is KtEscapeStringTemplateEntry)
KotlinStringULiteralExpression(entry, givenParent, entry.unescapedValue)
else
KotlinStringULiteralExpression(entry, givenParent)
}
}
}
}
internal fun convertExpression(expression: KtExpression,
givenParent: UElement?,
requiredType: Class<out UElement>? = null): UExpression? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
return { ctor(expression as P, givenParent) }
}
return with (requiredType) { when (expression) {
is KtVariableDeclaration -> expr<UDeclarationsExpression>(build(::convertVariablesDeclaration))
is KtStringTemplateExpression -> {
when {
expression.entries.isEmpty() -> {
expr<ULiteralExpression> { KotlinStringULiteralExpression(expression, givenParent, "") }
}
expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType)
else -> {
expr<UExpression> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
}
}
}
is KtDestructuringDeclaration -> expr<UDeclarationsExpression> {
val declarationsExpression = KotlinUDestructuringDeclarationExpression(givenParent, expression)
declarationsExpression.apply {
val tempAssignment = KotlinULocalVariable(UastKotlinPsiVariable.create(expression, declarationsExpression), expression, declarationsExpression)
val destructuringAssignments = expression.entries.mapIndexed { i, entry ->
val psiFactory = KtPsiFactory(expression.project)
val initializer = psiFactory.createAnalyzableExpression("${tempAssignment.name}.component${i + 1}()",
expression.containingFile)
initializer.destructuringDeclarationInitializer = true
KotlinULocalVariable(UastKotlinPsiVariable.create(entry, tempAssignment.psi, declarationsExpression, initializer), entry, declarationsExpression)
}
declarations = listOf(tempAssignment) + destructuringAssignments
}
}
is KtLabeledExpression -> expr<ULabeledExpression>(build(::KotlinULabeledExpression))
is KtClassLiteralExpression -> expr<UClassLiteralExpression>(build(::KotlinUClassLiteralExpression))
is KtObjectLiteralExpression -> expr<UObjectLiteralExpression>(build(::KotlinUObjectLiteralExpression))
is KtDotQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUQualifiedReferenceExpression))
is KtSafeQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUSafeQualifiedExpression))
is KtSimpleNameExpression -> expr<USimpleNameReferenceExpression>(build(::KotlinUSimpleReferenceExpression))
is KtCallExpression -> expr<UCallExpression>(build(::KotlinUFunctionCallExpression))
is KtCollectionLiteralExpression -> expr<UCallExpression>(build(::KotlinUCollectionLiteralExpression))
is KtBinaryExpression -> {
if (expression.operationToken == KtTokens.ELVIS) {
expr<UExpressionList>(build(::createElvisExpression))
}
else expr<UBinaryExpression>(build(::KotlinUBinaryExpression))
}
is KtParenthesizedExpression -> expr<UParenthesizedExpression>(build(::KotlinUParenthesizedExpression))
is KtPrefixExpression -> expr<UPrefixExpression>(build(::KotlinUPrefixExpression))
is KtPostfixExpression -> expr<UPostfixExpression>(build(::KotlinUPostfixExpression))
is KtThisExpression -> expr<UThisExpression>(build(::KotlinUThisExpression))
is KtSuperExpression -> expr<USuperExpression>(build(::KotlinUSuperExpression))
is KtCallableReferenceExpression -> expr<UCallableReferenceExpression>(build(::KotlinUCallableReferenceExpression))
is KtIsExpression -> expr<UBinaryExpressionWithType>(build(::KotlinUTypeCheckExpression))
is KtIfExpression -> expr<UIfExpression>(build(::KotlinUIfExpression))
is KtWhileExpression -> expr<UWhileExpression>(build(::KotlinUWhileExpression))
is KtDoWhileExpression -> expr<UDoWhileExpression>(build(::KotlinUDoWhileExpression))
is KtForExpression -> expr<UForEachExpression>(build(::KotlinUForEachExpression))
is KtWhenExpression -> expr<USwitchExpression>(build(::KotlinUSwitchExpression))
is KtBreakExpression -> expr<UBreakExpression>(build(::KotlinUBreakExpression))
is KtContinueExpression -> expr<UContinueExpression>(build(::KotlinUContinueExpression))
is KtReturnExpression -> expr<UReturnExpression>(build(::KotlinUReturnExpression))
is KtThrowExpression -> expr<UThrowExpression>(build(::KotlinUThrowExpression))
is KtBlockExpression -> expr<UBlockExpression>(build(::KotlinUBlockExpression))
is KtConstantExpression -> expr<ULiteralExpression>(build(::KotlinULiteralExpression))
is KtTryExpression -> expr<UTryExpression>(build(::KotlinUTryExpression))
is KtArrayAccessExpression -> expr<UArrayAccessExpression>(build(::KotlinUArrayAccessExpression))
is KtLambdaExpression -> expr<ULambdaExpression>(build(::KotlinULambdaExpression))
is KtBinaryExpressionWithTypeRHS -> expr<UBinaryExpressionWithType>(build(::KotlinUBinaryExpressionWithType))
is KtClassOrObject -> expr<UDeclarationsExpression> {
expression.toLightClass()?.let { lightClass ->
KotlinUDeclarationsExpression(givenParent).apply {
declarations = listOf(KotlinUClass.create(lightClass, this))
}
} ?: UastEmptyExpression
}
is KtFunction -> if (expression.name.isNullOrEmpty()) {
expr<ULambdaExpression>(build(::createLocalFunctionLambdaExpression))
}
else {
expr<UDeclarationsExpression>(build(::createLocalFunctionDeclaration))
}
else -> expr<UExpression>(build(::UnknownKotlinExpression))
}}
}
internal fun convertWhenCondition(condition: KtWhenCondition,
givenParent: UElement?,
requiredType: Class<out UElement>? = null): UExpression? {
return with(requiredType) {
when (condition) {
is KtWhenConditionInRange -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpression(condition, givenParent).apply {
leftOperand = KotlinStringUSimpleReferenceExpression("it", this)
operator = when {
condition.isNegated -> KotlinBinaryOperators.NOT_IN
else -> KotlinBinaryOperators.IN
}
rightOperand = KotlinConverter.convertOrEmpty(condition.rangeExpression, this)
}
}
is KtWhenConditionIsPattern -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpressionWithType(condition, givenParent).apply {
operand = KotlinStringUSimpleReferenceExpression("it", this)
operationKind = when {
condition.isNegated -> KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK
else -> UastBinaryExpressionWithTypeKind.INSTANCE_CHECK
}
val typeRef = condition.typeReference
typeReference = typeRef?.let {
LazyKotlinUTypeReferenceExpression(it, this) { typeRef.toPsiType(this, boxed = true) }
}
}
}
is KtWhenConditionWithExpression ->
condition.expression?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) }
else -> expr<UExpression> { UastEmptyExpression }
}
}
}
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression {
return expression?.let { convertExpression(it, parent, null) } ?: UastEmptyExpression
}
internal fun convertOrNull(expression: KtExpression?, parent: UElement?): UExpression? {
return if (expression != null) convertExpression(expression, parent, null) else null
}
internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression =
createAnalyzableProperty("val x = $text", context).initializer ?: error("Failed to create expression from text: '$text'")
internal fun KtPsiFactory.createAnalyzableProperty(text: String, context: PsiElement): KtProperty =
createAnalyzableDeclaration(text, context)
internal fun <TDeclaration : KtDeclaration> KtPsiFactory.createAnalyzableDeclaration(text: String, context: PsiElement): TDeclaration {
val file = createAnalyzableFile("dummy.kt", text, context)
val declarations = file.declarations
assert(declarations.size == 1) { "${declarations.size} declarations in $text" }
return declarations.first() as TDeclaration
}
}
private fun convertVariablesDeclaration(
psi: KtVariableDeclaration,
parent: UElement?
): UDeclarationsExpression {
val declarationsExpression = parent as? KotlinUDeclarationsExpression
?: psi.parent.toUElementOfType<UDeclarationsExpression>() as? KotlinUDeclarationsExpression
?: KotlinUDeclarationsExpression(null, parent, psi)
val parentPsiElement = parent?.psi
val variable = KotlinUAnnotatedLocalVariable(
UastKotlinPsiVariable.create(psi, parentPsiElement, declarationsExpression), psi, declarationsExpression) { annotationParent ->
psi.annotationEntries.map { KotlinUAnnotation(it, annotationParent) }
}
return declarationsExpression.apply { declarations = listOf(variable) }
}
@@ -0,0 +1,165 @@
package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.ResolveResult
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
import org.jetbrains.uast.kotlin.internal.multiResolveResults
abstract class KotlinUAnnotationBase<T : KtCallElement>(
final override val sourcePsi: T,
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UAnnotationEx, UAnchorOwner, UMultiResolvable {
abstract override val javaPsi: PsiAnnotation?
final override val psi: PsiElement = sourcePsi
protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget?
private val resolvedCall: ResolvedCall<*>? get () = sourcePsi.getResolvedCall(sourcePsi.analyze())
override val qualifiedName: String? by lz {
computeClassDescriptor().takeUnless(ErrorUtils::isError)
?.fqNameUnsafe
?.takeIf(FqNameUnsafe::isSafe)
?.toSafe()
?.toString()
}
override val attributeValues: List<UNamedExpression> by lz {
resolvedCall?.valueArguments?.entries?.mapNotNull {
val arguments = it.value.arguments
val name = it.key.name.asString()
when {
arguments.size == 1 ->
KotlinUNamedExpression.create(name, arguments.first(), this)
arguments.size > 1 ->
KotlinUNamedExpression.create(name, arguments, this)
else -> null
}
} ?: emptyList()
}
protected abstract fun computeClassDescriptor(): ClassDescriptor?
override fun resolve(): PsiClass? = computeClassDescriptor()?.toSource()?.getMaybeLightElement(this) as? PsiClass
override fun findAttributeValue(name: String?): UExpression? =
findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value")
fun findAttributeValueExpression(arg: ValueArgument): UExpression? {
val mapping = resolvedCall?.getArgumentMapping(arg)
return (mapping as? ArgumentMatch)?.let { match ->
val namedExpression = attributeValues.find { it.name == match.valueParameter.name.asString() }
namedExpression?.expression as? KotlinUVarargExpression ?: namedExpression
}
}
override fun findDeclaredAttributeValue(name: String?): UExpression? {
return attributeValues.find {
it.name == name ||
(name == null && it.name == "value") ||
(name == "value" && it.name == null)
}?.expression
}
private fun findAttributeDefaultValue(name: String): UExpression? {
val parameter = computeClassDescriptor()
?.unsubstitutedPrimaryConstructor
?.valueParameters
?.find { it.name.asString() == name } ?: return null
val defaultValue = (parameter.source.getPsi() as? KtParameter)?.defaultValue ?: return null
return getLanguagePlugin().convertWithParent(defaultValue)
}
override fun convertParent(): UElement? {
val superParent = super.convertParent() ?: return null
if (annotationUseSiteTarget() == AnnotationUseSiteTarget.RECEIVER) {
(superParent.uastParent as? KotlinUMethod)?.uastParameters?.firstIsInstance<KotlinReceiverUParameter>()?.let {
return it
}
}
return superParent
}
override fun multiResolve(): Iterable<ResolveResult> = sourcePsi.multiResolveResults().asIterable()
}
class KotlinUAnnotation(
annotationEntry: KtAnnotationEntry,
givenParent: UElement?
) : KotlinUAnnotationBase<KtAnnotationEntry>(annotationEntry, givenParent), UAnnotation {
override val javaPsi = annotationEntry.toLightAnnotation()
override fun computeClassDescriptor(): ClassDescriptor? =
sourcePsi.analyze()[BindingContext.ANNOTATION, sourcePsi]?.annotationClass
override fun annotationUseSiteTarget() = sourcePsi.useSiteTarget?.getAnnotationUseSiteTarget()
override val uastAnchor by lazy {
KotlinUIdentifier(
javaPsi?.nameReferenceElement,
annotationEntry.typeReference?.typeElement?.let {
(it as? KtUserType)?.referenceExpression?.getReferencedNameElement() ?: it.navigationElement
},
this
)
}
}
class KotlinUNestedAnnotation private constructor(
original: KtCallExpression,
givenParent: UElement?
) : KotlinUAnnotationBase<KtCallExpression>(original, givenParent) {
override val javaPsi: PsiAnnotation? by lazy { original.toLightAnnotation() }
override fun computeClassDescriptor(): ClassDescriptor? = classDescriptor(sourcePsi)
override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null
override val uastAnchor by lazy {
KotlinUIdentifier(
javaPsi?.nameReferenceElement?.referenceNameElement,
(original.calleeExpression as? KtNameReferenceExpression)?.getReferencedNameElement(),
this
)
}
companion object {
fun tryCreate(original: KtCallExpression, givenParent: UElement?): KotlinUNestedAnnotation? {
if (classDescriptor(original)?.kind == ClassKind.ANNOTATION_CLASS)
return KotlinUNestedAnnotation(original, givenParent)
else
return null
}
private fun classDescriptor(original: KtCallExpression) =
(original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass
}
}
@@ -25,11 +25,12 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UImportStatement
import org.jetbrains.uast.USimpleNameReferenceExpression
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUImportStatement(
override val psi: KtImportDirective,
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UImportStatement {
) : KotlinAbstractUElement(givenParent), UImportStatement, DelegatedMultiResolve {
override val javaPsi = null
@@ -35,6 +35,7 @@ import org.jetbrains.uast.*
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable
import org.jetbrains.uast.visitor.UastVisitor
@@ -113,6 +114,7 @@ abstract class AbstractKotlinUVariable(givenParent: UElement?) : KotlinAbstractU
override fun equals(other: Any?) = other is AbstractKotlinUVariable && psi == other.psi
class WrappedUAnnotation(psiAnnotation: PsiAnnotation, override val uastParent: UElement) : UAnnotation, UAnchorOwner,
DelegatedMultiResolve,
JvmDeclarationUElementPlaceholder {
override val javaPsi: PsiAnnotation = psiAnnotation
@@ -234,7 +236,7 @@ class KotlinReceiverUParameter(
}
class KotlinNullabilityUAnnotation(val annotatedElement: PsiElement, override val uastParent: UElement) : UAnnotationEx, UAnchorOwner,
JvmDeclarationUElementPlaceholder {
DelegatedMultiResolve, JvmDeclarationUElementPlaceholder {
private fun getTargetType(annotatedElement: PsiElement): KotlinType? {
if (annotatedElement is KtTypeReference) {
@@ -380,7 +382,7 @@ class KotlinUEnumConstant(
psi: PsiEnumConstant,
override val sourcePsi: KtElement?,
givenParent: UElement?
) : AbstractKotlinUVariable(givenParent), UEnumConstant, UCallExpressionEx, PsiEnumConstant by psi {
) : AbstractKotlinUVariable(givenParent), UEnumConstant, UCallExpressionEx, DelegatedMultiResolve, PsiEnumConstant by psi {
override val initializingClass: UClass? by lz {
(psi.initializingClass as? KtLightClass)?.let { initializingClass ->
@@ -0,0 +1,55 @@
/*
* 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 com.intellij.psi.ResolveResult
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS
import org.jetbrains.uast.UCallableReferenceExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UMultiResolvable
import org.jetbrains.uast.kotlin.internal.getResolveResultVariants
class KotlinUCallableReferenceExpression(
override val psi: KtCallableReferenceExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UCallableReferenceExpression, UMultiResolvable, KotlinUElementWithType {
override val qualifierExpression: UExpression?
get() {
if (qualifierType != null) return null
val receiverExpression = psi.receiverExpression ?: return null
return KotlinConverter.convertExpression(receiverExpression, this)
}
override val qualifierType by lz {
val ktType = psi.analyze()[DOUBLE_COLON_LHS, psi.receiverExpression]?.type ?: return@lz null
ktType.toPsiType(this, psi, boxed = true)
}
override val callableName: String
get() = psi.callableReference.getReferencedName()
override val resolvedName: String?
get() = (resolve() as? PsiNamedElement)?.name
override fun resolve() = psi.callableReference.resolveCallToDeclaration(this)
override fun multiResolve(): Iterable<ResolveResult> = getResolveResultVariants(psi.callableReference)
}
@@ -14,11 +14,12 @@ import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.KotlinAbstractUExpression
import org.jetbrains.uast.kotlin.KotlinConverter
import org.jetbrains.uast.kotlin.KotlinUElementWithType
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUCollectionLiteralExpression(
override val sourcePsi: KtCollectionLiteralExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, KotlinUElementWithType {
) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, DelegatedMultiResolve, KotlinUElementWithType {
override val classReference: UReferenceExpression? get() = null
@@ -0,0 +1,248 @@
/*
* 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.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiType
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
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
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.uast.*
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.TypedResolveResult
import org.jetbrains.uast.kotlin.internal.getReferenceVariants
import org.jetbrains.uast.kotlin.internal.multiResolveResults
import org.jetbrains.uast.visitor.UastVisitor
class KotlinUFunctionCallExpression(
override val psi: KtCallElement,
givenParent: UElement?,
private val _resolvedCall: ResolvedCall<*>?
) : KotlinAbstractUExpression(givenParent), UCallExpressionEx, KotlinUElementWithType, UMultiResolvable {
constructor(psi: KtCallElement, uastParent: UElement?) : this(psi, uastParent, null)
private val resolvedCall
get() = _resolvedCall ?: psi.getResolvedCall(psi.analyze())
override val receiverType by lz {
val resolvedCall = this.resolvedCall ?: return@lz null
val receiver = resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver ?: return@lz null
receiver.type.toPsiType(this, psi, boxed = true)
}
override val methodName by lz { resolvedCall?.resultingDescriptor?.name?.asString() }
override val classReference by lz {
KotlinClassViaConstructorUSimpleReferenceExpression(psi, methodName.orAnonymous("class"), this)
}
override val methodIdentifier by lz {
val calleeExpression = psi.calleeExpression
when (calleeExpression) {
null -> null
is KtNameReferenceExpression ->
KotlinUIdentifier(calleeExpression.getReferencedNameElement(), this)
is KtConstructorDelegationReferenceExpression ->
KotlinUIdentifier(calleeExpression.firstChild ?: calleeExpression, this)
is KtConstructorCalleeExpression ->
KotlinUIdentifier(
calleeExpression.constructorReferenceExpression?.getReferencedNameElement() ?: calleeExpression, this
)
else -> KotlinUIdentifier(calleeExpression, this)
}
}
override val valueArgumentCount: Int
get() = psi.valueArguments.size
override val valueArguments by lz { psi.valueArguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } }
override fun getArgumentForParameter(i: Int): UExpression? {
val resolvedCall = resolvedCall
if (resolvedCall != null) {
val actualParamIndex = if (resolvedCall.extensionReceiver == null) i else i - 1
if (actualParamIndex == -1) return receiver
return getArgumentExpressionByIndex(actualParamIndex, resolvedCall, this)
}
val argument = valueArguments.getOrNull(i) ?: return null
val argumentType = argument.getExpressionType()
for (resolveResult in multiResolve()) {
val psiMethod = resolveResult.element as? PsiMethod ?: continue
val psiParameter = psiMethod.parameterList.parameters.getOrNull(i) ?: continue
if (argumentType == null || psiParameter.type.isAssignableFrom(argumentType))
return argument
}
return null
}
override fun getExpressionType(): PsiType? {
super<KotlinUElementWithType>.getExpressionType()?.let { return it }
for (resolveResult in multiResolve()) {
val psiMethod = resolveResult.element
when {
psiMethod.isConstructor ->
psiMethod.containingClass?.let { return PsiTypesUtil.getClassType(it) }
else ->
psiMethod.returnType?.let { return it }
}
}
return null
}
override val typeArgumentCount: Int
get() = psi.typeArguments.size
override val typeArguments by lz { psi.typeArguments.map { it.typeReference.toPsiType(this, boxed = true) } }
override val returnType: PsiType?
get() = getExpressionType()
override val kind: UastCallKind by lz {
val resolvedCall = resolvedCall ?: return@lz UastCallKind.METHOD_CALL
when {
resolvedCall.resultingDescriptor is ConstructorDescriptor -> UastCallKind.CONSTRUCTOR_CALL
this.isAnnotationArgumentArrayInitializer() -> UastCallKind.NESTED_ARRAY_INITIALIZER
else -> UastCallKind.METHOD_CALL
}
}
override val receiver: UExpression?
get() {
(uastParent as? UQualifiedReferenceExpression)?.let {
if (it.selector == this) return it.receiver
}
val ktNameReferenceExpression = psi.calleeExpression as? KtNameReferenceExpression ?: return null
val variableCallDescriptor =
(resolvedCall as? VariableAsFunctionResolvedCall)?.variableCall?.resultingDescriptor
?: (resolvedCall?.resultingDescriptor as? FunctionDescriptor)?.takeIf { it.visibility == Visibilities.LOCAL }
?: return null
// an implicit receiver for variables calls (KT-25524)
return object : KotlinAbstractUExpression(this), UReferenceExpression {
private val resolvedDeclaration = variableCallDescriptor.toSource()
override val psi: KtNameReferenceExpression get() = ktNameReferenceExpression
override val resolvedName: String? get() = (resolvedDeclaration as? PsiNamedElement)?.name
override fun resolve(): PsiElement? = resolvedDeclaration
}
}
private val multiResolved by lazy(fun(): Iterable<TypedResolveResult<PsiMethod>> {
val contextElement = psi
if (!Registry.`is`("kotlin.uast.multiresolve.enabled", true)) {
val calleeExpression = contextElement.calleeExpression ?: return emptyList()
return calleeExpression.multiResolveResults()
.mapNotNull { it.element.safeAs<PsiMethod>()?.let { TypedResolveResult(it) } }
.asIterable()
}
val calleeExpression = contextElement.calleeExpression as? KtReferenceExpression ?: return emptyList()
val methodName = methodName ?: calleeExpression.text ?: return emptyList()
val variants = getReferenceVariants(calleeExpression, methodName)
return variants.flatMap {
when (val source = it.toSource()) {
is KtClass -> source.toLightClass()?.constructors?.asSequence().orEmpty()
else -> resolveSource(psi, it, source)?.let { sequenceOf(it) }.orEmpty()
}
}.map { TypedResolveResult(it) }.asIterable()
})
override fun multiResolve(): Iterable<TypedResolveResult<PsiMethod>> = multiResolved
override fun resolve(): PsiMethod? {
val descriptor = resolvedCall?.resultingDescriptor ?: return null
val source = descriptor.toSource()
return resolveSource(psi, descriptor, source)
}
override fun accept(visitor: UastVisitor) {
if (visitor.visitCallExpression(this)) return
methodIdentifier?.accept(visitor)
classReference.accept(visitor)
valueArguments.acceptList(visitor)
visitor.afterVisitCallExpression(this)
}
private fun isAnnotationArgumentArrayInitializer(): Boolean {
val resolvedCall = resolvedCall ?: return false
// KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call
return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)
}
override fun convertParent(): UElement? = super.convertParent().let { result ->
when (result) {
is UMethod -> result.uastBody ?: result
is UClass ->
result.methods
.filterIsInstance<KotlinConstructorUMethod>()
.firstOrNull { it.isPrimary }
?.uastBody
?: result
else -> result
}
}
}
internal fun getArgumentExpressionByIndex(
actualParamIndex: Int,
resolvedCall: ResolvedCall<out CallableDescriptor>,
parent: UElement
): UExpression? {
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, parent)
}
return KotlinConverter.convertOrEmpty(expression, parent)
}
return createVarargsHolder(arguments, parent)
}
private fun createVarargsHolder(arguments: List<ValueArgument>, parent: UElement?): KotlinUExpressionList =
KotlinUExpressionList(null, UastSpecialExpressionKind.VARARGS, parent).apply {
expressions = arguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), parent) }
}
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUNamedExpression private constructor(
override val name: String?,
@@ -60,7 +61,7 @@ class KotlinUNamedExpression private constructor(
class KotlinUVarargExpression(
private val valueArgs: List<ValueArgument>,
uastParent: UElement?
) : KotlinAbstractUExpression(uastParent), UCallExpressionEx {
) : KotlinAbstractUExpression(uastParent), UCallExpressionEx, DelegatedMultiResolve {
override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER
override val valueArguments: List<UExpression> by lz {
@@ -23,11 +23,12 @@ import org.jetbrains.kotlin.psi.KtObjectLiteralExpression
import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUObjectLiteralExpression(
override val psi: KtObjectLiteralExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UObjectLiteralExpression, UCallExpressionEx, KotlinUElementWithType {
) : KotlinAbstractUExpression(givenParent), UObjectLiteralExpression, UCallExpressionEx, DelegatedMultiResolve, KotlinUElementWithType {
override val declaration: UClass by lz {
psi.objectDeclaration.toLightClass()
@@ -21,11 +21,13 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtPostfixExpression
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUPostfixExpression(
override val psi: KtPostfixExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UPostfixExpression, KotlinUElementWithType, KotlinEvaluatableUElement, UResolvable {
) : KotlinAbstractUExpression(givenParent), UPostfixExpression, KotlinUElementWithType, KotlinEvaluatableUElement,
UResolvable, DelegatedMultiResolve {
override val operand by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) }
override val operator = when (psi.operationToken) {
@@ -25,11 +25,12 @@ import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
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,
) : 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) }
@@ -41,13 +42,14 @@ class KotlinUQualifiedReferenceExpression(
get() = (resolve() as? PsiNamedElement)?.name
}
//TODO maybe remove it if it is unused?
class KotlinUComponentQualifiedReferenceExpression(
override val psi: KtDestructuringDeclarationEntry,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UQualifiedReferenceExpression,
KotlinUElementWithType, KotlinEvaluatableUElement {
override val psi: KtDestructuringDeclarationEntry,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UQualifiedReferenceExpression, DelegatedMultiResolve,
KotlinUElementWithType, KotlinEvaluatableUElement {
override val accessType = UastQualifiedExpressionAccessType.SIMPLE
override lateinit var receiver: UExpression
internal set
@@ -56,7 +58,7 @@ class KotlinUComponentQualifiedReferenceExpression(
override val resolvedName: String?
get() = psi.analyze()[BindingContext.COMPONENT_RESOLVED_CALL, psi]?.resultingDescriptor?.name?.asString()
override fun resolve(): PsiElement? {
val bindingContext = psi.analyze()
val descriptor = bindingContext[BindingContext.COMPONENT_RESOLVED_CALL, psi]?.resultingDescriptor ?: return null
@@ -0,0 +1,41 @@
/*
* 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 com.intellij.psi.ResolveResult
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UMultiResolvable
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.kotlin.internal.getResolveResultVariants
class KotlinUSafeQualifiedExpression(
override val psi: KtSafeQualifiedExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UQualifiedReferenceExpression, UMultiResolvable,
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 = KotlinQualifiedExpressionAccessTypes.SAFE
override val resolvedName: String?
get() = (resolve() as? PsiNamedElement)?.name
override fun resolve() = psi.selectorExpression?.resolveCallToDeclaration(this)
override fun multiResolve(): Iterable<ResolveResult> = getResolveResultVariants(psi.selectorExpression)
}
@@ -32,6 +32,7 @@ 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(
@@ -102,7 +103,7 @@ open class KotlinUSimpleReferenceExpression(
private val resolvedCall: ResolvedCall<*>,
private val accessorDescriptor: DeclarationDescriptor,
val setterValue: KtExpression?
) : UCallExpressionEx, JvmDeclarationUElementPlaceholder {
) : UCallExpressionEx, DelegatedMultiResolve, JvmDeclarationUElementPlaceholder {
override val methodName: String?
get() = accessorDescriptor.name.asString()
@@ -22,11 +22,12 @@ import org.jetbrains.uast.UElement
import org.jetbrains.uast.UIdentifier
import org.jetbrains.uast.USuperExpression
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUSuperExpression(
override val psi: KtSuperExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), USuperExpression, KotlinUElementWithType, KotlinEvaluatableUElement {
) : KotlinAbstractUExpression(givenParent), USuperExpression, DelegatedMultiResolve, KotlinUElementWithType, KotlinEvaluatableUElement {
override val label: String?
get() = psi.getLabelName()
@@ -22,11 +22,12 @@ import org.jetbrains.uast.UElement
import org.jetbrains.uast.UIdentifier
import org.jetbrains.uast.UThisExpression
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUThisExpression(
override val psi: KtThisExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UThisExpression, KotlinUElementWithType, KotlinEvaluatableUElement {
) : KotlinAbstractUExpression(givenParent), UThisExpression, DelegatedMultiResolve, KotlinUElementWithType, KotlinEvaluatableUElement {
override val label: String?
get() = psi.getLabelName()
@@ -0,0 +1,38 @@
/*
* 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 org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UIdentifier
import org.jetbrains.uast.UThisExpression
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.internal.DelegatedMultiResolve
class KotlinUThisExpression(
override val psi: KtThisExpression,
givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), UThisExpression, DelegatedMultiResolve, KotlinUElementWithType, KotlinEvaluatableUElement {
override val label: String?
get() = psi.getLabelName()
override val labelIdentifier: UIdentifier?
get() = psi.getTargetLabel()?.let { KotlinUIdentifier(it, this) }
override fun resolve() = psi.analyze()[BindingContext.LABEL_TARGET, psi.getTargetLabel()]
}
@@ -0,0 +1,92 @@
package org.jetbrains.uast.kotlin.internal
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.codegen.ClassBuilderMode
import org.jetbrains.kotlin.codegen.state.IncompatibleClassTracker
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
class CliKotlinUastResolveProviderService : KotlinUastResolveProviderService {
val Project.analysisCompletedHandler: UastAnalysisHandlerExtension?
get() = getExtensions(AnalysisHandlerExtension.extensionPointName)
.filterIsInstance<UastAnalysisHandlerExtension>()
.firstOrNull()
override fun getBindingContext(element: KtElement): BindingContext {
return element.project.analysisCompletedHandler?.getBindingContext() ?: BindingContext.EMPTY
}
override fun getTypeMapper(element: KtElement): KotlinTypeMapper? {
return element.project.analysisCompletedHandler?.getTypeMapper()
}
override fun isJvmElement(psiElement: PsiElement) = true
override fun getLanguageVersionSettings(element: KtElement): LanguageVersionSettings {
return element.project.analysisCompletedHandler?.getLanguageVersionSettings() ?: LanguageVersionSettingsImpl.DEFAULT
}
override fun getReferenceVariants(ktElement: KtElement, nameHint: String): Sequence<DeclarationDescriptor> =
emptySequence() // Not supported
}
class UastAnalysisHandlerExtension : AnalysisHandlerExtension {
private var context: BindingContext? = null
private var typeMapper: KotlinTypeMapper? = null
private var languageVersionSettings: LanguageVersionSettings? = null
fun getBindingContext() = context
fun getLanguageVersionSettings() = languageVersionSettings
fun getTypeMapper(): KotlinTypeMapper? {
if (typeMapper != null) return typeMapper
val bindingContext = context ?: return null
val typeMapper = KotlinTypeMapper(
bindingContext, ClassBuilderMode.LIGHT_CLASSES,
IncompatibleClassTracker.DoNothing, JvmAbi.DEFAULT_MODULE_NAME, JvmTarget.DEFAULT, KotlinTypeMapper.RELEASE_COROUTINES_DEFAULT,
false
)
this.typeMapper = typeMapper
return typeMapper
}
override fun doAnalysis(
project: Project,
module: ModuleDescriptor,
projectContext: ProjectContext,
files: Collection<KtFile>,
bindingTrace: BindingTrace,
componentProvider: ComponentProvider
): AnalysisResult? {
languageVersionSettings = componentProvider.get<LanguageVersionSettings>()
return super.doAnalysis(project, module, projectContext, files, bindingTrace, componentProvider)
}
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? {
context = bindingTrace.bindingContext
return null
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.kotlin.internal
import org.jetbrains.uast.UResolvable
//Dummy holder until idea 183
interface DelegatedMultiResolve : UResolvable
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.kotlin.internal
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiPolyVariantReference
import com.intellij.psi.PsiSubstitutor
import com.intellij.psi.ResolveResult
import com.intellij.psi.infos.CandidateInfo
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UMultiResolvable
import org.jetbrains.uast.UResolvable
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
import org.jetbrains.uast.kotlin.getMaybeLightElement
import org.jetbrains.uast.kotlin.toSource
internal fun getReferenceVariants(ktElement: KtElement, nameHint: String): Sequence<DeclarationDescriptor> =
ServiceManager.getService(ktElement.project, KotlinUastResolveProviderService::class.java).getReferenceVariants(ktElement, nameHint)
internal fun UElement.getResolveResultVariants(ktExpression: KtExpression?): Iterable<ResolveResult> {
ktExpression ?: return emptyList()
if (!Registry.`is`("kotlin.uast.multiresolve.enabled", true)) return ktExpression.multiResolveResults().asIterable()
val referenceVariants = getReferenceVariants(ktExpression, ktExpression.name ?: ktExpression.text)
fun asCandidateInfo(descriptor: DeclarationDescriptor): CandidateInfo? =
descriptor.toSource()?.getMaybeLightElement(this)?.let { CandidateInfo(it, PsiSubstitutor.EMPTY) }
return referenceVariants.mapNotNull(::asCandidateInfo).asIterable()
}
internal fun KtElement.multiResolveResults(): Sequence<ResolveResult> =
references.asSequence().flatMap { ref ->
when (ref) {
is PsiPolyVariantReference -> ref.multiResolve(false).asSequence()
else -> (ref.resolve()?.let { sequenceOf(CandidateInfo(it, PsiSubstitutor.EMPTY)) }).orEmpty()
}
}
interface DelegatedMultiResolve : UMultiResolvable, UResolvable {
override fun multiResolve(): Iterable<ResolveResult> = listOfNotNull(resolve()?.let { CandidateInfo(it, PsiSubstitutor.EMPTY) })
}
class TypedResolveResult<T : PsiElement>(element: T) : CandidateInfo(element, PsiSubstitutor.EMPTY) {
@Suppress("UNCHECKED_CAST")
override fun getElement(): T = super.getElement() as T
}
@@ -16,7 +16,6 @@
package org.jetbrains.uast.test.kotlin
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiLanguageInjectionHost
@@ -131,19 +130,6 @@ class KotlinDetachedUastTest : KotlinLightCodeInsightFixtureTestCase() {
}
fun testResolveStringFromUast() {
val file = myFixture.addFileToProject(
"s.kt", """fun foo(){
val s = "abc"
s.toUpperCase()
}
""${'"'}"""
)
val refs = file.findUElementByTextFromPsi<UQualifiedReferenceExpression>("s.toUpperCase()")
TestCase.assertNotNull((refs.receiver.getExpressionType() as PsiClassType).resolve())
}
}
fun <T> T?.orFail(msg: String): T = this ?: error(msg)
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.PsiClassType
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.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.test.env.findUElementByTextFromPsi
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()")
TestCase.assertNotNull((refs.receiver.getExpressionType() as PsiClassType).resolve())
}
}
@@ -0,0 +1,293 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.openapi.util.registry.Registry
import com.intellij.psi.PsiClassType
import com.intellij.psi.PsiType
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.uast.UElement
import org.jetbrains.uast.UQualifiedReferenceExpression
import org.jetbrains.uast.getContainingUMethod
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
import org.jetbrains.uast.test.env.findElementByText
import org.jetbrains.uast.test.env.findElementByTextFromPsi
import org.jetbrains.uast.test.env.findUElementByTextFromPsi
import org.jetbrains.uast.toUElement
class KotlinUastResolveApiTest : KotlinLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor =
KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
override fun setUp() {
super.setUp()
Registry.get("kotlin.uast.multiresolve.enabled").setValue(true, testRootDisposable)
}
fun testResolveStringFromUast() {
val file = myFixture.addFileToProject(
"s.kt", """fun foo(){
val s = "abc"
s.toUpperCase()
}
""${'"'}"""
)
val refs = file.findUElementByTextFromPsi<UQualifiedReferenceExpression>("s.toUpperCase()")
TestCase.assertNotNull((refs.receiver.getExpressionType() as PsiClassType).resolve())
}
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(""
}
"""
)
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>("\"\"")
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(""
}
"""
)
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>("\"\"")
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())
}
}