diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.181 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.181 new file mode 100644 index 00000000000..9e43f412f48 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/internal/kotlinInternalUastUtils.kt.181 @@ -0,0 +1,213 @@ +/* + * 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.components.ServiceManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiPrimitiveType +import com.intellij.psi.PsiType +import com.intellij.psi.impl.cache.TypeInfo +import com.intellij.psi.impl.compiled.ClsTypeElementImpl +import com.intellij.psi.impl.compiled.SignatureParsing +import com.intellij.psi.impl.compiled.StubBuildingVisitor +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiTypesUtil +import org.jetbrains.kotlin.asJava.elements.FakeFileForLightClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype +import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.types.typeUtil.isInterface +import org.jetbrains.uast.* +import java.lang.ref.WeakReference +import java.text.StringCharacterIterator + +internal val KOTLIN_CACHED_UELEMENT_KEY = Key.create>("cached-kotlin-uelement") + +@Suppress("NOTHING_TO_INLINE") +internal inline fun String?.orAnonymous(kind: String = ""): String = this ?: "" + +internal fun DeclarationDescriptor.toSource(): PsiElement? { + return try { + DescriptorToSourceUtils.getEffectiveReferencedDescriptors(this) + .asSequence() + .mapNotNull { DescriptorToSourceUtils.getSourceFromDescriptor(it) } + .firstOrNull() + } + catch (e: Exception) { + Logger.getInstance("DeclarationDescriptor.toSource").error(e) + null + } +} + +internal fun lz(initializer: () -> T) = lazy(LazyThreadSafetyMode.SYNCHRONIZED, initializer) + +internal fun KotlinType.toPsiType(source: UElement, element: KtElement, boxed: Boolean): PsiType { + if (this.isError) return UastErrorType + + (constructor.declarationDescriptor as? TypeAliasDescriptor)?.let { typeAlias -> + return typeAlias.expandedType.toPsiType(source, element, boxed) + } + + if (arguments.isEmpty()) { + val typeFqName = this.constructor.declarationDescriptor?.fqNameSafe?.asString() + fun PsiPrimitiveType.orBoxed() = if (boxed) getBoxedType(element) else this + val psiType = when (typeFqName) { + "kotlin.Int" -> PsiType.INT.orBoxed() + "kotlin.Long" -> PsiType.LONG.orBoxed() + "kotlin.Short" -> PsiType.SHORT.orBoxed() + "kotlin.Boolean" -> PsiType.BOOLEAN.orBoxed() + "kotlin.Byte" -> PsiType.BYTE.orBoxed() + "kotlin.Char" -> PsiType.CHAR.orBoxed() + "kotlin.Double" -> PsiType.DOUBLE.orBoxed() + "kotlin.Float" -> PsiType.FLOAT.orBoxed() + "kotlin.String" -> PsiType.getJavaLangString(element.manager, GlobalSearchScope.projectScope(element.project)) + else -> { + val typeConstructor = this.constructor + if (typeConstructor is IntegerValueTypeConstructor) { + typeConstructor.supertypes.first().toPsiType(source, element, boxed) + } else { + null + } + } + } + if (psiType != null) return psiType + } + + if (this.containsLocalTypes()) return UastErrorType + + val project = element.project + val typeMapper = ServiceManager.getService(project, KotlinUastBindingContextProviderService::class.java) + .getTypeMapper(element) ?: return UastErrorType + + val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.TYPE) + val typeMappingMode = if (boxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT + typeMapper.mapType(this, signatureWriter, typeMappingMode) + + val signature = StringCharacterIterator(signatureWriter.toString()) + + val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER) + val typeInfo = TypeInfo.fromString(javaType, false) + val typeText = TypeInfo.createTypeText(typeInfo) ?: return UastErrorType + + return ClsTypeElementImpl(source.getParentOfType(false)?.psi ?: element, typeText, '\u0000').type +} + +private fun KotlinType.containsLocalTypes(): Boolean { + val typeDeclarationDescriptor = this.constructor.declarationDescriptor + if (typeDeclarationDescriptor is ClassDescriptor && DescriptorUtils.isLocal(typeDeclarationDescriptor)) { + return true + } + + return arguments.any { !it.isStarProjection && it.type.containsLocalTypes() } +} + +internal fun KtTypeReference?.toPsiType(source: UElement, boxed: Boolean = false): PsiType { + if (this == null) return UastErrorType + return (analyze()[BindingContext.TYPE, this] ?: return UastErrorType).toPsiType(source, this, boxed) +} + +internal fun KtClassOrObject.toPsiType(): PsiType { + val lightClass = toLightClass() ?: return UastErrorType + return PsiTypesUtil.getClassType(lightClass) +} + +internal fun PsiElement.getMaybeLightElement(context: UElement): PsiElement? { + return when (this) { + is KtVariableDeclaration -> { + val lightElement = toLightElements().firstOrNull() + if (lightElement != null) return lightElement + + val languagePlugin = context.getLanguagePlugin() + val uElement = languagePlugin.convertElementWithParent(this, null) + when (uElement) { + is UDeclaration -> uElement.psi + is UDeclarationsExpression -> uElement.declarations.firstOrNull()?.psi + else -> null + } + } + is KtDeclaration -> toLightElements().firstOrNull() + is KtElement -> null + else -> this + } +} + +internal fun KtElement.resolveCallToDeclaration( + context: KotlinAbstractUElement, + resultingDescriptor: DeclarationDescriptor? = null +): PsiElement? { + val descriptor = resultingDescriptor ?: run { + val resolvedCall = getResolvedCall(analyze()) ?: return null + resolvedCall.resultingDescriptor + } + + return descriptor.toSource()?.getMaybeLightElement(context) +} + +internal fun KtExpression.unwrapBlockOrParenthesis(): KtExpression { + val innerExpression = KtPsiUtil.safeDeparenthesize(this) + if (innerExpression is KtBlockExpression) { + val statement = innerExpression.statements.singleOrNull() ?: return this + return KtPsiUtil.safeDeparenthesize(statement) + } + return innerExpression +} + +internal fun KtElement.analyze(): BindingContext { + if(containingFile !is KtFile) return BindingContext.EMPTY // EA-114080, EA-113475 + return ServiceManager.getService(project, KotlinUastBindingContextProviderService::class.java) + ?.getBindingContext(this) ?: BindingContext.EMPTY +} + +internal inline fun unwrap(element: P): P { + val unwrapped = if (element is T) element.psi else element + assert(unwrapped !is UElement) + return unwrapped as P +} + +internal fun KtExpression.getExpectedType(): KotlinType? = analyze()[BindingContext.EXPECTED_EXPRESSION_TYPE, this] + +internal fun KtTypeReference.getType(): KotlinType? = analyze()[BindingContext.TYPE, this] + +internal fun KotlinType.getFunctionalInterfaceType(source: UElement, element: KtElement): PsiType? = + takeIf { it.isInterface() && !it.isBuiltinFunctionalTypeOrSubtype }?.toPsiType(source, element, false) + +internal fun KotlinULambdaExpression.getFunctionalInterfaceType(): PsiType? { + val parent = psi.parent + return when(parent) { + is KtBinaryExpressionWithTypeRHS -> parent.right?.getType()?.getFunctionalInterfaceType(this, psi) + else -> psi.getExpectedType()?.getFunctionalInterfaceType(this, psi) + } +} + +internal fun unwrapFakeFileForLightClass(file: PsiFile): PsiFile = (file as? FakeFileForLightClass)?.ktFile ?: file