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.0.0
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.JetNodeTypes
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens
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 JetCallElement.getCallNameExpression(): JetSimpleNameExpression? {
    val calleeExpression = getCalleeExpression() ?: return null

    return when (calleeExpression) {
        is JetSimpleNameExpression -> calleeExpression
        is JetConstructorCalleeExpression -> calleeExpression.getConstructorReferenceExpression()
        else -> null
    }
}

/**
 * Returns enclosing qualifying element for given [[JetSimpleNameExpression]]
 * ([[JetQualifiedExpression]] or [[JetUserType]] or original expression)
 */
public fun JetSimpleNameExpression.getQualifiedElement(): JetElement {
    val baseExpression = (parent as? JetCallExpression) ?: this
    val parent = baseExpression.parent
    return when (parent) {
        is JetQualifiedExpression -> if (parent.selectorExpression == baseExpression) parent else baseExpression
        is JetUserType -> if (parent.referenceExpression == baseExpression) parent else baseExpression
        else -> baseExpression
    }
}

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

/**
 * Returns rightmost selector of the qualified element (null if there is no such selector)
 */
public fun JetElement.getQualifiedElementSelector(): JetElement? {
    return when (this) {
        is JetSimpleNameExpression -> this
        is JetCallExpression -> getCalleeExpression()
        is JetQualifiedExpression -> {
            val selector = getSelectorExpression()
            if (selector is JetCallExpression) selector.getCalleeExpression() else selector
        }
        is JetUserType -> getReferenceExpression()
        else -> null
    }
}

public fun JetSimpleNameExpression.getReceiverExpression(): JetExpression? {
    val parent = getParent()
    when {
        parent is JetQualifiedExpression -> {
            val receiverExpression = parent.getReceiverExpression()
            // Name expression can't be receiver for itself
            if (receiverExpression != this) {
                return receiverExpression
            }
        }
        parent is JetCallExpression -> {
            //This is in case `a().b()`
            val callExpression = parent
            val grandParent = callExpression.getParent()
            if (grandParent is JetQualifiedExpression) {
                val parentsReceiver = grandParent.getReceiverExpression()
                if (parentsReceiver != callExpression) {
                    return parentsReceiver
                }
            }
        }
        parent is JetBinaryExpression && parent.getOperationReference() == this -> {
            return if (parent.getOperationToken() in OperatorConventions.IN_OPERATIONS) parent.getRight() else parent.getLeft()
        }
        parent is JetUnaryExpression && parent.getOperationReference() == this -> {
            return parent.getBaseExpression()!!
        }
        parent is JetUserType -> {
            val qualifier = parent.getQualifier()
            if (qualifier != null) {
                return qualifier.getReferenceExpression()!!
            }
        }
    }
    return null
}

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

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

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

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

public fun JetExpression.isDotReceiver(): Boolean =
        (getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this

public fun JetElement.getCalleeHighlightingRange(): TextRange {
    val annotationEntry: JetAnnotationEntry =
            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 JetElement.blockExpressionsOrSingle(): Sequence =
        if (this is JetBlockExpression) getStatements().asSequence() else sequenceOf(this)

public fun JetExpression.lastBlockStatementOrThis(): JetExpression
        = (this as? JetBlockExpression)?.getStatements()?.lastOrNull() ?: this

public fun JetBlockExpression.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 JetClass.isInheritable(): Boolean {
    return isInterface() || hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.ABSTRACT_KEYWORD)
}

public fun JetDeclaration.isOverridable(): Boolean {
    val parent = getParent()
    if (!(parent is JetClassBody || parent is JetParameterList)) return false

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

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

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

public fun JetClass.isAbstract(): Boolean = isInterface() || hasModifier(JetTokens.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 JetFile) {
            val directive = file.findImportByAlias(referencedName)
            if (directive != null) {
                var reference = directive.getImportedReference()
                while (reference is JetDotQualifiedExpression) {
                    reference = reference.getSelectorExpression()
                }
                if (reference is JetSimpleNameExpression) {
                    result.add(reference.getReferencedName())
                }
            }
        }
    }

    assertTrue(this is JetClassOrObject)

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

    val specifiers = (this as JetClassOrObject).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 JetExpression.getAnnotationEntries(): List {
    val parent = getParent()
    return when (parent) {
        is JetAnnotatedExpression -> parent.getAnnotationEntries()
        is JetLabeledExpression -> parent.getAnnotationEntries()
        else -> emptyList()
    }
}

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

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

private fun JetAnnotationsContainer.collectAnnotationEntriesFromPsi(): List {
    return getChildren().flatMap { child ->
        when (child) {
            is JetAnnotationEntry -> listOf(child)
            is JetAnnotation -> 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): JetVisitorVoid {
    return object : JetTreeVisitorVoid() {
        override fun visitJetElement(element: JetElement) {
            super.visitJetElement(element)
            if (element is T) {
                block(element)
            }
        }
    }
}

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

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

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

public fun JetDeclaration.isExtensionDeclaration(): Boolean {
    val callable: JetCallableDeclaration? = when (this) {
        is JetNamedFunction, is JetProperty -> this as JetCallableDeclaration
        is JetPropertyAccessor -> getNonStrictParentOfType()
        else -> null
    }

    return callable?.getReceiverTypeReference() != null
}

public fun JetClassOrObject.isObjectLiteral(): Boolean = this is JetObjectDeclaration && isObjectLiteral()

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

public fun JetModifierListOwner.isPrivate(): Boolean = hasModifier(JetTokens.PRIVATE_KEYWORD)

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

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

public fun JetExpression.isFunctionLiteralOutsideParentheses(): Boolean {
    val parent = getParent()
    return when (parent) {
        is JetFunctionLiteralArgument -> true
        is JetLabeledExpression -> parent.isFunctionLiteralOutsideParentheses()
        else -> false
    }
}

public fun JetExpression.getAssignmentByLHS(): JetBinaryExpression? {
    val parent = getParent() as? JetBinaryExpression ?: return null
    return if (JetPsiUtil.isAssignment(parent) && parent.getLeft() == this) parent else null
}

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

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

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

public fun JetNamedDeclaration.getValueParameterList(): JetParameterList? {
    return when (this) {
        is JetCallableDeclaration -> getValueParameterList()
        is JetClass -> getPrimaryConstructorParameterList()
        else -> null
    }
}

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

public fun JetExpression.asAssignment(): JetBinaryExpression? =
        if (JetPsiUtil.isAssignment(this)) this as JetBinaryExpression else null

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

public fun JetDeclaration.visibilityModifierType(): JetModifierKeywordToken?
        = visibilityModifier()?.node?.elementType as JetModifierKeywordToken?

public fun JetStringTemplateExpression.isPlain() = entries.all { it is JetLiteralStringTemplateEntry }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy