All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.psi.psiUtil.jetPsiUtil.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * 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.kotlin.psi.psiUtil

import com.intellij.extapi.psi.StubBasedPsiElementBase
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiParameter
import com.intellij.psi.PsiParameterList
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.stubs.StubElement
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
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.types.expressions.OperatorConventions
import java.util.*
import kotlin.test.assertTrue

// NOTE: in this file we collect only Kotlin-specific methods working with PSI and not modifying it

// ----------- Calls and qualified expressions ---------------------------------------------------------------------------------------------

public fun KtCallElement.getCallNameExpression(): KtSimpleNameExpression? {
    val calleeExpression = getCalleeExpression() ?: return null

    return when (calleeExpression) {
        is KtSimpleNameExpression -> calleeExpression
        is KtConstructorCalleeExpression -> calleeExpression.getConstructorReferenceExpression()
        else -> null
    }
}

/**
 * Returns enclosing qualifying element for given [[KtSimpleNameExpression]]
 * ([[KtQualifiedExpression]] or [[KtUserType]] or original expression)
 */
public fun KtSimpleNameExpression.getQualifiedElement(): KtElement {
    val baseExpression = (parent as? KtCallExpression) ?: this
    val parent = baseExpression.parent
    return when (parent) {
        is KtQualifiedExpression -> if (parent.selectorExpression == baseExpression) parent else baseExpression
        is KtUserType -> if (parent.referenceExpression == baseExpression) parent else baseExpression
        else -> baseExpression
    }
}

public fun KtSimpleNameExpression.getTopmostParentQualifiedExpressionForSelector(): KtQualifiedExpression? {
    return sequence(this) {
        val parentQualified = it.getParent() as? KtQualifiedExpression
        if (parentQualified?.getSelectorExpression() == it) parentQualified else null
    }.last() as? KtQualifiedExpression
}

/**
 * Returns rightmost selector of the qualified element (null if there is no such selector)
 */
public fun KtElement.getQualifiedElementSelector(): KtElement? {
    return when (this) {
        is KtSimpleNameExpression -> this
        is KtCallExpression -> getCalleeExpression()
        is KtQualifiedExpression -> {
            val selector = getSelectorExpression()
            if (selector is KtCallExpression) selector.getCalleeExpression() else selector
        }
        is KtUserType -> getReferenceExpression()
        else -> null
    }
}

public fun KtSimpleNameExpression.getReceiverExpression(): KtExpression? {
    val parent = getParent()
    when {
        parent is KtQualifiedExpression -> {
            val receiverExpression = parent.getReceiverExpression()
            // Name expression can't be receiver for itself
            if (receiverExpression != this) {
                return receiverExpression
            }
        }
        parent is KtCallExpression -> {
            //This is in case `a().b()`
            val callExpression = parent
            val grandParent = callExpression.getParent()
            if (grandParent is KtQualifiedExpression) {
                val parentsReceiver = grandParent.getReceiverExpression()
                if (parentsReceiver != callExpression) {
                    return parentsReceiver
                }
            }
        }
        parent is KtBinaryExpression && parent.getOperationReference() == this -> {
            return if (parent.getOperationToken() in OperatorConventions.IN_OPERATIONS) parent.getRight() else parent.getLeft()
        }
        parent is KtUnaryExpression && parent.getOperationReference() == this -> {
            return parent.getBaseExpression()!!
        }
        parent is KtUserType -> {
            val qualifier = parent.getQualifier()
            if (qualifier != null) {
                return qualifier.getReferenceExpression()!!
            }
        }
    }
    return null
}

public fun KtElement.getQualifiedExpressionForSelector(): KtQualifiedExpression? {
    val parent = getParent()
    return if (parent is KtQualifiedExpression && parent.getSelectorExpression() == this) parent else null
}

public fun KtExpression.getQualifiedExpressionForSelectorOrThis(): KtExpression {
    return getQualifiedExpressionForSelector() ?: this
}

public fun KtExpression.getQualifiedExpressionForReceiver(): KtQualifiedExpression? {
    val parent = getParent()
    return if (parent is KtQualifiedExpression && parent.getReceiverExpression() == this) parent else null
}

public fun KtExpression.getQualifiedExpressionForReceiverOrThis(): KtExpression {
    return getQualifiedExpressionForReceiver() ?: this
}

public fun KtExpression.isDotReceiver(): Boolean =
        (getParent() as? KtDotQualifiedExpression)?.getReceiverExpression() == this

public fun KtElement.getCalleeHighlightingRange(): TextRange {
    val annotationEntry: KtAnnotationEntry =
            PsiTreeUtil.getParentOfType(
                    this, javaClass(), /* strict = */false, javaClass()
            ) ?: return getTextRange()

    val startOffset = annotationEntry.getAtSymbol()?.getTextRange()?.getStartOffset()
                      ?: annotationEntry.getCalleeExpression()!!.startOffset

    return TextRange(startOffset, annotationEntry.getCalleeExpression()!!.endOffset)
}

// ---------- Block expression -------------------------------------------------------------------------------------------------------------

public fun KtElement.blockExpressionsOrSingle(): Sequence =
        if (this is KtBlockExpression) getStatements().asSequence() else sequenceOf(this)

public fun KtExpression.lastBlockStatementOrThis(): KtExpression
        = (this as? KtBlockExpression)?.getStatements()?.lastOrNull() ?: this

public fun KtBlockExpression.contentRange(): PsiChildRange {
    val first = (getLBrace()?.getNextSibling() ?: getFirstChild())
            ?.siblings(withItself = false)
            ?.firstOrNull { it !is PsiWhiteSpace }
    val rBrace = getRBrace()
    if (first == rBrace) return PsiChildRange.EMPTY
    val last = rBrace!!
            .siblings(forward = false, withItself = false)
            .first { it !is PsiWhiteSpace }
    return PsiChildRange(first, last)
}

// ----------- Inheritance -----------------------------------------------------------------------------------------------------------------

public fun KtClass.isInheritable(): Boolean {
    return isInterface() || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.ABSTRACT_KEYWORD)
}

public fun KtDeclaration.isOverridable(): Boolean {
    val parent = getParent()
    if (!(parent is KtClassBody || parent is KtParameterList)) return false

    val klass = parent.getParent() as? KtClass ?: return false
    if (!klass.isInheritable() && !klass.isEnum()) return false

    if (hasModifier(KtTokens.FINAL_KEYWORD) || hasModifier(KtTokens.PRIVATE_KEYWORD)) return false

    return klass.isInterface() ||
           hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.OVERRIDE_KEYWORD)
}

public fun KtClass.isAbstract(): Boolean = isInterface() || hasModifier(KtTokens.ABSTRACT_KEYWORD)

/**
 * Returns the list of unqualified names that are indexed as the superclass names of this class. For the names that might be imported
 * via an aliased import, includes both the original and the aliased name (reference resolution during inheritor search will sort this out).
 *
 * @return the list of possible superclass names
 */
public fun StubBasedPsiElementBase>.getSuperNames(): List {
    fun addSuperName(result: MutableList, referencedName: String): Unit {
        result.add(referencedName)

        val file = getContainingFile()
        if (file is KtFile) {
            val directive = file.findImportByAlias(referencedName)
            if (directive != null) {
                var reference = directive.getImportedReference()
                while (reference is KtDotQualifiedExpression) {
                    reference = reference.getSelectorExpression()
                }
                if (reference is KtSimpleNameExpression) {
                    result.add(reference.getReferencedName())
                }
            }
        }
    }

    assertTrue(this is KtClassOrObject)

    val stub = getStub()
    if (stub != null) {
        return stub.getSuperNames()
    }

    val specifiers = (this as KtClassOrObject).getDelegationSpecifiers()
    if (specifiers.isEmpty()) return Collections.emptyList()

    val result = ArrayList()
    for (specifier in specifiers) {
        val superType = specifier.getTypeAsUserType()
        if (superType != null) {
            val referencedName = superType.getReferencedName()
            if (referencedName != null) {
                addSuperName(result, referencedName)
            }
        }
    }

    return result
}

// ------------ Annotations ----------------------------------------------------------------------------------------------------------------

// Annotations on labeled expression lies on it's base expression
public fun KtExpression.getAnnotationEntries(): List {
    val parent = getParent()
    return when (parent) {
        is KtAnnotatedExpression -> parent.getAnnotationEntries()
        is KtLabeledExpression -> parent.getAnnotationEntries()
        else -> emptyList()
    }
}

public fun KtAnnotationsContainer.collectAnnotationEntriesFromStubOrPsi(): List {
    return when (this) {
        is StubBasedPsiElementBase<*> -> getStub()?.collectAnnotationEntriesFromStubElement() ?: collectAnnotationEntriesFromPsi()
        else -> collectAnnotationEntriesFromPsi()
    }
}

private fun StubElement<*>.collectAnnotationEntriesFromStubElement(): List {
    return getChildrenStubs().flatMap {
        child ->
        when (child.getStubType()) {
            KtNodeTypes.ANNOTATION_ENTRY -> listOf(child.getPsi() as KtAnnotationEntry)
            KtNodeTypes.ANNOTATION -> (child.getPsi() as KtAnnotation).getEntries()
            else -> emptyList()
        }
    }
}

private fun KtAnnotationsContainer.collectAnnotationEntriesFromPsi(): List {
    return getChildren().flatMap { child ->
        when (child) {
            is KtAnnotationEntry -> listOf(child)
            is KtAnnotation -> child.getEntries()
            else -> emptyList()
        }
    }
}

// -------- Recursive tree visiting --------------------------------------------------------------------------------------------------------

// Calls `block` on each descendant of T type
// Note, that calls happen in order of DFS-exit, so deeper nodes are applied earlier
public inline fun  forEachDescendantOfTypeVisitor(noinline block: (T) -> Unit): KtVisitorVoid {
    return object : KtTreeVisitorVoid() {
        override fun visitJetElement(element: KtElement) {
            super.visitJetElement(element)
            if (element is T) {
                block(element)
            }
        }
    }
}

public inline fun  flatMapDescendantsOfTypeVisitor(accumulator: MutableCollection, noinline map: (T) -> Collection): KtVisitorVoid {
    return forEachDescendantOfTypeVisitor { accumulator.addAll(map(it)) }
}

// ----------- Other -----------------------------------------------------------------------------------------------------------------------

public fun KtClassOrObject.effectiveDeclarations(): List {
    return when(this) {
        is KtClass -> getDeclarations() + getPrimaryConstructorParameters().filter { p -> p.hasValOrVar() }
        else -> getDeclarations()
    }
}

public fun KtDeclaration.isExtensionDeclaration(): Boolean {
    val callable: KtCallableDeclaration? = when (this) {
        is KtNamedFunction, is KtProperty -> this as KtCallableDeclaration
        is KtPropertyAccessor -> getNonStrictParentOfType()
        else -> null
    }

    return callable?.getReceiverTypeReference() != null
}

public fun KtClassOrObject.isObjectLiteral(): Boolean = this is KtObjectDeclaration && isObjectLiteral()

//TODO: strange method, and not only Kotlin specific (also Java)
public fun PsiElement.parameterIndex(): Int {
    val parent = getParent()
    return when {
        this is KtParameter && parent is KtParameterList -> parent.getParameters().indexOf(this)
        this is PsiParameter && parent is PsiParameterList -> parent.getParameterIndex(this)
        else -> -1
    }
}

public fun KtModifierListOwner.isPrivate(): Boolean = hasModifier(KtTokens.PRIVATE_KEYWORD)

public fun KtSimpleNameExpression.isImportDirectiveExpression(): Boolean {
    val parent = getParent()
    return parent is KtImportDirective || parent?.getParent() is KtImportDirective
}

public fun KtSimpleNameExpression.isPackageDirectiveExpression(): Boolean {
    val parent = getParent()
    return parent is KtPackageDirective || parent?.getParent() is KtPackageDirective
}

public fun KtExpression.isFunctionLiteralOutsideParentheses(): Boolean {
    val parent = getParent()
    return when (parent) {
        is KtFunctionLiteralArgument -> true
        is KtLabeledExpression -> parent.isFunctionLiteralOutsideParentheses()
        else -> false
    }
}

public fun KtExpression.getAssignmentByLHS(): KtBinaryExpression? {
    val parent = getParent() as? KtBinaryExpression ?: return null
    return if (KtPsiUtil.isAssignment(parent) && parent.getLeft() == this) parent else null
}

public fun KtStringTemplateExpression.getContentRange(): TextRange {
    val start = getNode().getFirstChildNode().getTextLength()
    val lastChild = getNode().getLastChildNode()
    val length = getTextLength()
    return TextRange(start, if (lastChild.getElementType() == KtTokens.CLOSING_QUOTE) length - lastChild.getTextLength() else length)
}

public fun KtStringTemplateExpression.isSingleQuoted(): Boolean
        = getNode().getFirstChildNode().getTextLength() == 1

public fun KtNamedDeclaration.getValueParameters(): List {
    return getValueParameterList()?.getParameters() ?: Collections.emptyList()
}

public fun KtNamedDeclaration.getValueParameterList(): KtParameterList? {
    return when (this) {
        is KtCallableDeclaration -> getValueParameterList()
        is KtClass -> getPrimaryConstructorParameterList()
        else -> null
    }
}

public fun KtFunctionLiteralArgument.getFunctionLiteralArgumentName(bindingContext: BindingContext): Name? {
    val callExpression = getParent() as KtCallExpression
    val resolvedCall = callExpression.getResolvedCall(bindingContext)
    return (resolvedCall?.getArgumentMapping(this) as? ArgumentMatch)?.valueParameter?.getName()
}

public fun KtExpression.asAssignment(): KtBinaryExpression? =
        if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null

public fun KtDeclaration.visibilityModifier(): PsiElement? {
    val modifierList = modifierList ?: return null
    return KtTokens.VISIBILITY_MODIFIERS.types
                   .asSequence()
                   .map { modifierList.getModifier(it as KtModifierKeywordToken) }
                   .firstOrNull { it != null }
}

public fun KtDeclaration.visibilityModifierType(): KtModifierKeywordToken?
        = visibilityModifier()?.node?.elementType as KtModifierKeywordToken?

public fun KtStringTemplateExpression.isPlain() = entries.all { it is KtLiteralStringTemplateEntry }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy