From 0ee98a8d1d32354c4c9d02b0977344ed87a8746b Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Wed, 7 Nov 2018 20:11:48 +0300 Subject: [PATCH] 183: Uast: multiresolve (KT-27244) --- idea/src/META-INF/jvm.xml | 6 + ...deaKotlinUastResolveProviderService.kt.183 | 65 ++ .../kotlin/KotlinUastLanguagePlugin.kt.183 | 557 ++++++++++++++++++ .../declarations/KotlinUAnnotation.kt.183 | 165 ++++++ .../declarations/KotlinUImportStatement.kt | 3 +- .../kotlin/declarations/KotlinUVariable.kt | 6 +- .../KotlinUCallableReferenceExpression.kt.183 | 55 ++ .../KotlinUCollectionLiteralExpression.kt | 3 +- .../KotlinUFunctionCallExpression.kt.183 | 248 ++++++++ .../expressions/KotlinUNamedExpression.kt | 3 +- .../KotlinUObjectLiteralExpression.kt | 3 +- .../expressions/KotlinUPostfixExpression.kt | 4 +- .../KotlinUQualifiedReferenceExpression.kt | 16 +- .../KotlinUSafeQualifiedExpression.kt.183 | 41 ++ .../KotlinUSimpleReferenceExpression.kt | 3 +- .../expressions/KotlinUSuperExpression.kt | 3 +- .../expressions/KotlinUThisExpression.kt | 3 +- .../expressions/KotlinUThisExpression.kt.183 | 38 ++ ...CliKotlinUastResolveProviderService.kt.183 | 92 +++ .../kotlin/internal/kotlinUastMultiresolve.kt | 14 + .../internal/kotlinUastMultiresolve.kt.183 | 59 ++ .../tests/KotlinDetachedUastTest.kt | 14 - .../tests/KotlinUastResolveApiTest.kt | 34 ++ .../tests/KotlinUastResolveApiTest.kt.183 | 293 +++++++++ 24 files changed, 1697 insertions(+), 31 deletions(-) create mode 100644 plugins/uast-kotlin-idea/src/IdeaKotlinUastResolveProviderService.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSafeQualifiedExpression.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/CliKotlinUastResolveProviderService.kt.183 create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt.183 create mode 100644 plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt create mode 100644 plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt.183 diff --git a/idea/src/META-INF/jvm.xml b/idea/src/META-INF/jvm.xml index 41065356acb..157a2004487 100644 --- a/idea/src/META-INF/jvm.xml +++ b/idea/src/META-INF/jvm.xml @@ -142,6 +142,12 @@ description="Whether to use an experimental implementation of Kotlin-as-Java classes" defaultValue="false" restartRequired="false"/> + + diff --git a/plugins/uast-kotlin-idea/src/IdeaKotlinUastResolveProviderService.kt.183 b/plugins/uast-kotlin-idea/src/IdeaKotlinUastResolveProviderService.kt.183 new file mode 100644 index 00000000000..4723e815851 --- /dev/null +++ b/plugins/uast-kotlin-idea/src/IdeaKotlinUastResolveProviderService.kt.183 @@ -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 { + val resolutionFacade = ktElement.getResolutionFacade() + val bindingContext = ktElement.analyze() + val call = ktElement.getCall(bindingContext) ?: return emptySequence() + return call.resolveCandidates(bindingContext, resolutionFacade).map { it.candidateDescriptor }.asSequence() + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.183 new file mode 100644 index 00000000000..028c0fb21b0 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.183 @@ -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 +} + +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?): UElement? { + if (!element.isJvmElement) return null + return convertDeclarationOrElement(element, parent, requiredType) + } + + override fun convertElementWithParent(element: PsiElement, requiredType: Class?): 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?): 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?): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) } + + fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = + { ctor(element as P, ktElement, givenParent) } + + fun

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(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 + is KtLightClass -> when (original.kotlinOrigin) { + is KtEnumEntry -> el { + convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) + } + else -> el { KotlinUClass.create(original, givenParent) } + } + is KtLightFieldImpl.KtLightEnumConstant -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) + is KtLightField -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) + is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) + is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) + is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinUVariable)) + + is KtEnumEntry -> el { + convertEnumEntry(original, givenParent) + } + is KtClassOrObject -> el { + original.toLightClass()?.let { lightClass -> + KotlinUClass.create(lightClass, givenParent) + } + } + is KtFunction -> + if (original.isLocal) { + el { + 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 { + val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null + convertDeclaration(lightMethod, givenParent, requiredType) + } + } + + is KtPropertyAccessor -> el { + 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 { + 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 { KotlinUFile(original, this@KotlinUastLanguagePlugin) } + is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } + is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) + is KtCallExpression -> + if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) { + el { 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 Class?.el(f: () -> UElement?): UElement? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +internal inline fun Class?.expr(f: () -> UExpression?): UExpression? { + return if (this == null || isAssignableFrom(ActualT::class.java)) f() else null +} + +private fun convertNonLocalProperty(property: KtProperty, + givenParent: UElement?, + requiredType: Class?): UElement? { + val methods = LightClassUtil.getLightClassPropertyMethods(property) + return methods.backingField?.let { backingField -> + with(requiredType) { + el { 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?): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { + return { ctor(element as P, givenParent) } + } + + return with (requiredType) { when (element) { + is KtParameterList -> el { + val declarationsExpression = KotlinUDeclarationsExpression(givenParent) + declarationsExpression.apply { + declarations = element.parameters.mapIndexed { i, p -> + KotlinUParameter(UastKotlinPsiParameter.create(p, element, declarationsExpression, i), p, this) + } + } + } + is KtClassBody -> el(build(KotlinUExpressionList.Companion::createClassBody)) + is KtCatchClause -> el(build(::KotlinUCatchClause)) + is KtVariableDeclaration -> + if (element is KtProperty && !element.isLocal) { + el { + LightClassUtil.getLightClassBackingField(element)?.let { + KotlinUField(it, element, givenParent) + } + } + } + else { + el { + 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 { UastEmptyExpression(givenParent) } + } + } + is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el(build(::KotlinStringULiteralExpression)) + is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, requiredType) } ?: expr { UastEmptyExpression } + is KtWhenEntry -> el(build(::KotlinUSwitchEntry)) + is KtWhenCondition -> convertWhenCondition(element, givenParent, requiredType) + is KtTypeReference -> el { LazyKotlinUTypeReferenceExpression(element, givenParent) } + is KtConstructorDelegationCall -> + el { KotlinUFunctionCallExpression(element, givenParent) } + is KtSuperTypeCallEntry -> + el { + (element.getParentOfType(true)?.parent as? KtObjectLiteralExpression) + ?.toUElementOfType() + ?: KotlinUFunctionCallExpression(element, givenParent) + } + is KtImportDirective -> el(build(::KotlinUImportStatement)) + else -> { + if (element is LeafPsiElement) { + if (element.elementType in identifiersTokens) + if (element.elementType != KtTokens.OBJECT_KEYWORD || element.getParentOfType(false)?.nameIdentifier == null) + el(build(::KotlinUIdentifier)) + else null + else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression) + el(build(::KotlinUIdentifier)) + else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression) + el { + UIdentifier( + element, + KotlinUCollectionLiteralExpression( + element.parent as KtCollectionLiteralExpression, + null + ) + ) + } + else null + } else null + } + }} + } + + + internal fun convertEntry(entry: KtStringTemplateEntry, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + return with(requiredType) { + if (entry is KtStringTemplateEntryWithExpression) { + expr { + KotlinConverter.convertOrEmpty(entry.expression, givenParent) + } + } + else { + expr { + if (entry is KtEscapeStringTemplateEntry) + KotlinStringULiteralExpression(entry, givenParent, entry.unescapedValue) + else + KotlinStringULiteralExpression(entry, givenParent) + } + } + } + } + + internal fun convertExpression(expression: KtExpression, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { + return { ctor(expression as P, givenParent) } + } + + return with (requiredType) { when (expression) { + is KtVariableDeclaration -> expr(build(::convertVariablesDeclaration)) + + is KtStringTemplateExpression -> { + when { + expression.entries.isEmpty() -> { + expr { KotlinStringULiteralExpression(expression, givenParent, "") } + } + expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType) + else -> { + expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } + } + } + } + is KtDestructuringDeclaration -> expr { + 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(build(::KotlinULabeledExpression)) + is KtClassLiteralExpression -> expr(build(::KotlinUClassLiteralExpression)) + is KtObjectLiteralExpression -> expr(build(::KotlinUObjectLiteralExpression)) + is KtDotQualifiedExpression -> expr(build(::KotlinUQualifiedReferenceExpression)) + is KtSafeQualifiedExpression -> expr(build(::KotlinUSafeQualifiedExpression)) + is KtSimpleNameExpression -> expr(build(::KotlinUSimpleReferenceExpression)) + is KtCallExpression -> expr(build(::KotlinUFunctionCallExpression)) + is KtCollectionLiteralExpression -> expr(build(::KotlinUCollectionLiteralExpression)) + is KtBinaryExpression -> { + if (expression.operationToken == KtTokens.ELVIS) { + expr(build(::createElvisExpression)) + } + else expr(build(::KotlinUBinaryExpression)) + } + is KtParenthesizedExpression -> expr(build(::KotlinUParenthesizedExpression)) + is KtPrefixExpression -> expr(build(::KotlinUPrefixExpression)) + is KtPostfixExpression -> expr(build(::KotlinUPostfixExpression)) + is KtThisExpression -> expr(build(::KotlinUThisExpression)) + is KtSuperExpression -> expr(build(::KotlinUSuperExpression)) + is KtCallableReferenceExpression -> expr(build(::KotlinUCallableReferenceExpression)) + is KtIsExpression -> expr(build(::KotlinUTypeCheckExpression)) + is KtIfExpression -> expr(build(::KotlinUIfExpression)) + is KtWhileExpression -> expr(build(::KotlinUWhileExpression)) + is KtDoWhileExpression -> expr(build(::KotlinUDoWhileExpression)) + is KtForExpression -> expr(build(::KotlinUForEachExpression)) + is KtWhenExpression -> expr(build(::KotlinUSwitchExpression)) + is KtBreakExpression -> expr(build(::KotlinUBreakExpression)) + is KtContinueExpression -> expr(build(::KotlinUContinueExpression)) + is KtReturnExpression -> expr(build(::KotlinUReturnExpression)) + is KtThrowExpression -> expr(build(::KotlinUThrowExpression)) + is KtBlockExpression -> expr(build(::KotlinUBlockExpression)) + is KtConstantExpression -> expr(build(::KotlinULiteralExpression)) + is KtTryExpression -> expr(build(::KotlinUTryExpression)) + is KtArrayAccessExpression -> expr(build(::KotlinUArrayAccessExpression)) + is KtLambdaExpression -> expr(build(::KotlinULambdaExpression)) + is KtBinaryExpressionWithTypeRHS -> expr(build(::KotlinUBinaryExpressionWithType)) + is KtClassOrObject -> expr { + expression.toLightClass()?.let { lightClass -> + KotlinUDeclarationsExpression(givenParent).apply { + declarations = listOf(KotlinUClass.create(lightClass, this)) + } + } ?: UastEmptyExpression + } + is KtFunction -> if (expression.name.isNullOrEmpty()) { + expr(build(::createLocalFunctionLambdaExpression)) + } + else { + expr(build(::createLocalFunctionDeclaration)) + } + + else -> expr(build(::UnknownKotlinExpression)) + }} + } + + internal fun convertWhenCondition(condition: KtWhenCondition, + givenParent: UElement?, + requiredType: Class? = null): UExpression? { + return with(requiredType) { + when (condition) { + is KtWhenConditionInRange -> expr { + 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 { + 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 { 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 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() 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) } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.183 new file mode 100644 index 00000000000..8beae165279 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.183 @@ -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( + 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 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()?.let { + return it + } + } + return superParent + } + + override fun multiResolve(): Iterable = sourcePsi.multiResolveResults().asIterable() +} + +class KotlinUAnnotation( + annotationEntry: KtAnnotationEntry, + givenParent: UElement? +) : KotlinUAnnotationBase(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(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 + } + +} + + diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUImportStatement.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUImportStatement.kt index f78e2529739..fd8b911b262 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUImportStatement.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUImportStatement.kt @@ -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 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt index fae24c8c8d2..0f00ced650a 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt @@ -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 -> diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.183 new file mode 100644 index 00000000000..c0c89a33c91 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCallableReferenceExpression.kt.183 @@ -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 = getResolveResultVariants(psi.callableReference) + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt index f0d59284ab6..2571e0c350c 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUCollectionLiteralExpression.kt @@ -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 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.183 new file mode 100644 index 00000000000..389285b4a81 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUFunctionCallExpression.kt.183 @@ -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.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> { + val contextElement = psi + + if (!Registry.`is`("kotlin.uast.multiresolve.enabled", true)) { + val calleeExpression = contextElement.calleeExpression ?: return emptyList() + return calleeExpression.multiResolveResults() + .mapNotNull { it.element.safeAs()?.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> = 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() + .firstOrNull { it.isPrimary } + ?.uastBody + ?: result + else -> result + } + } + +} + +internal fun getArgumentExpressionByIndex( + actualParamIndex: Int, + resolvedCall: ResolvedCall, + 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, parent: UElement?): KotlinUExpressionList = + KotlinUExpressionList(null, UastSpecialExpressionKind.VARARGS, parent).apply { + expressions = arguments.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), parent) } + } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt index a4fac66330a..bf958b3de0a 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUNamedExpression.kt @@ -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, uastParent: UElement? -) : KotlinAbstractUExpression(uastParent), UCallExpressionEx { +) : KotlinAbstractUExpression(uastParent), UCallExpressionEx, DelegatedMultiResolve { override val kind: UastCallKind = UastCallKind.NESTED_ARRAY_INITIALIZER override val valueArguments: List by lz { diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt index 8802269b954..69be1b1d232 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUObjectLiteralExpression.kt @@ -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() diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt index 4d86d3b6bbf..daf5bffeb97 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUPostfixExpression.kt @@ -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) { diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUQualifiedReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUQualifiedReferenceExpression.kt index 208c99837f9..b917f9cb2b1 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUQualifiedReferenceExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUQualifiedReferenceExpression.kt @@ -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 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSafeQualifiedExpression.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSafeQualifiedExpression.kt.183 new file mode 100644 index 00000000000..d9e6401b70e --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSafeQualifiedExpression.kt.183 @@ -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 = getResolveResultVariants(psi.selectorExpression) +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt index 90248bf9e06..fddea33e1a4 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSimpleReferenceExpression.kt @@ -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() diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt index 9ae022699d3..7a26bbc6a05 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUSuperExpression.kt @@ -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() diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt index ce8ef9146a3..a126192989f 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt @@ -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() diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.183 new file mode 100644 index 00000000000..a126192989f --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUThisExpression.kt.183 @@ -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()] +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/CliKotlinUastResolveProviderService.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/CliKotlinUastResolveProviderService.kt.183 new file mode 100644 index 00000000000..adb2aa866ad --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/CliKotlinUastResolveProviderService.kt.183 @@ -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() + .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 = + 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, + bindingTrace: BindingTrace, + componentProvider: ComponentProvider + ): AnalysisResult? { + languageVersionSettings = componentProvider.get() + return super.doAnalysis(project, module, projectContext, files, bindingTrace, componentProvider) + } + + override fun analysisCompleted( + project: Project, + module: ModuleDescriptor, + bindingTrace: BindingTrace, + files: Collection + ): AnalysisResult? { + context = bindingTrace.bindingContext + return null + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt new file mode 100644 index 00000000000..fbcbffe6359 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt @@ -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 + + diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt.183 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt.183 new file mode 100644 index 00000000000..b8c9ef57de8 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinUastMultiresolve.kt.183 @@ -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 = + ServiceManager.getService(ktElement.project, KotlinUastResolveProviderService::class.java).getReferenceVariants(ktElement, nameHint) + +internal fun UElement.getResolveResultVariants(ktExpression: KtExpression?): Iterable { + 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 = + 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 = listOfNotNull(resolve()?.let { CandidateInfo(it, PsiSubstitutor.EMPTY) }) +} + +class TypedResolveResult(element: T) : CandidateInfo(element, PsiSubstitutor.EMPTY) { + @Suppress("UNCHECKED_CAST") + override fun getElement(): T = super.getElement() as T +} + diff --git a/plugins/uast-kotlin/tests/KotlinDetachedUastTest.kt b/plugins/uast-kotlin/tests/KotlinDetachedUastTest.kt index 65e2001a168..b8fa9b4c7c6 100644 --- a/plugins/uast-kotlin/tests/KotlinDetachedUastTest.kt +++ b/plugins/uast-kotlin/tests/KotlinDetachedUastTest.kt @@ -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("s.toUpperCase()") - TestCase.assertNotNull((refs.receiver.getExpressionType() as PsiClassType).resolve()) - } - } fun T?.orFail(msg: String): T = this ?: error(msg) \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt b/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt new file mode 100644 index 00000000000..d3bb4dd3e43 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt @@ -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("s.toUpperCase()") + TestCase.assertNotNull((refs.receiver.getExpressionType() as PsiClassType).resolve()) + } + +} diff --git a/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt.183 b/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt.183 new file mode 100644 index 00000000000..2841ad043a5 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastResolveApiTest.kt.183 @@ -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("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) { + foo(1 + }""" + ) + + val main = file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + val functionCall = + main.findElementByText("foo").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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("1") + val firstParameter = functionCall.getArgumentForParameter(0) + TestCase.assertEquals(firstArgument, firstParameter) + + } + + fun testMultiResolveJava() { + val file = myFixture.configureByText( + "s.kt", """ + + fun main(args: Array) { + System.out.print("" + } + """ + ) + + val main = file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + val functionCall = main.findElementByText("print").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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("\"\"") + 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) { + JavaClass().setParameter("" + } + """ + ) + + val main = file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + val functionCall = main.findElementByText("setParameter").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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("\"\"") + 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) { + MyClass().foo( + } + """ + ) + + + val functionCall = + file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + .findElementByText("foo").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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) { + MyClass( + } + """ + ) + + + val functionCall = + file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + .findElementByText("MyClass").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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) { + Foo( + } + """ + ) + + val functionCall = + file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + .findElementByText("Foo").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + 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) { + MyClass().foo( + }""" + ) + + val functionCall = + file.toUElement()!!.findElementByTextFromPsi("main").getContainingUMethod()!! + .findElementByText("foo").uastParent as KotlinUFunctionCallExpression + + val resolvedDeclaration = functionCall.multiResolve() + val resolvedDeclarationsStrings = resolvedDeclaration.map { it.element.text ?: "" } + assertContainsElements( + resolvedDeclarationsStrings, + "@JvmOverloads\n fun foo(i1: Int = 1, i2: Int = 2): Int = TODO()" + ) + TestCase.assertEquals(PsiType.INT, functionCall.getExpressionType()) + } + +} +