183: Uast: multiresolve (KT-27244)
This commit is contained in:
@@ -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) }
|
||||
}
|
||||
+165
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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 ->
|
||||
|
||||
+55
@@ -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)
|
||||
|
||||
}
|
||||
+2
-1
@@ -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
|
||||
|
||||
|
||||
+248
@@ -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) }
|
||||
}
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
+3
-1
@@ -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) {
|
||||
|
||||
+9
-7
@@ -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
|
||||
|
||||
+41
@@ -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)
|
||||
}
|
||||
+2
-1
@@ -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()
|
||||
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
|
||||
+2
-1
@@ -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()
|
||||
|
||||
|
||||
+38
@@ -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()]
|
||||
}
|
||||
+92
@@ -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
|
||||
|
||||
|
||||
+59
@@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user