diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt index 3cdea0024cf..def2b141388 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/jetPsiUtil.kt @@ -36,6 +36,8 @@ 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 @@ -46,15 +48,105 @@ public fun JetCallElement.getCallNameExpression(): JetSimpleNameExpression? { } } -public fun JetClassOrObject.effectiveDeclarations(): List = - when(this) { - is JetClass -> - getDeclarations() + getPrimaryConstructorParameters().filter { p -> p.hasValOrVarNode() } - else -> - getDeclarations() - } +/** + * Returns enclosing qualifying element for given [[JetSimpleNameExpression]] + * ([[JetQualifiedExpression]] or [[JetUserType]] or original expression) + */ +public fun JetSimpleNameExpression.getQualifiedElement(): JetElement { + val baseExpression: JetElement = (getParent() as? JetCallExpression) ?: this + val parent = baseExpression.getParent() + return when (parent) { + is JetQualifiedExpression -> if (parent.getSelectorExpression().isAncestor(baseExpression)) parent else baseExpression + is JetUserType -> if (parent.getReferenceExpression().isAncestor(baseExpression)) parent else baseExpression + else -> baseExpression + } +} -public fun JetClass.isAbstract(): Boolean = isInterface() || hasModifier(JetTokens.ABSTRACT_KEYWORD) +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 && !isImportDirectiveExpression() -> { + 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 JetElement.getQualifiedExpressionForSelectorOrThis(): JetElement { + return getQualifiedExpressionForSelector() ?: 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) @@ -62,6 +154,39 @@ public fun JetElement.blockExpressionsOrSingle(): Sequence = public fun JetExpression.lastBlockStatementOrThis(): JetExpression = (this as? JetBlockExpression)?.getStatements()?.lastIsInstanceOrNull() ?: 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() + if (!(klass is JetClass && klass.isInheritable())) 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). @@ -111,21 +236,72 @@ public fun StubBasedPsiElementBase { + val parent = getParent() + return when (parent) { + is JetAnnotatedExpression -> parent.getAnnotationEntries() + is JetLabeledExpression -> parent.getAnnotationEntries() + else -> emptyList() + } } -public fun JetDeclaration.isOverridable(): Boolean { - val parent = getParent() - if (!(parent is JetClassBody || parent is JetParameterList)) return false +public fun JetAnnotationsContainer.collectAnnotationEntriesFromStubOrPsi(): List { + return when (this) { + is StubBasedPsiElementBase<*> -> getStub()?.collectAnnotationEntriesFromStubElement() ?: collectAnnotationEntriesFromPsi() + else -> collectAnnotationEntriesFromPsi() + } +} - val klass = parent.getParent() - if (!(klass is JetClass && klass.isInheritable())) return false +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() + } + } +} - if (hasModifier(JetTokens.FINAL_KEYWORD) || hasModifier(JetTokens.PRIVATE_KEYWORD)) return false +private fun JetAnnotationsContainer.collectAnnotationEntriesFromPsi(): List { + return getChildren().flatMap { child -> + when (child) { + is JetAnnotationEntry -> listOf(child) + is JetAnnotation -> child.getEntries() + else -> emptyList() + } + } +} - return klass.isInterface() || - hasModifier(JetTokens.ABSTRACT_KEYWORD) || hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.OVERRIDE_KEYWORD) +// -------- 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.hasValOrVarNode() } + else -> getDeclarations() + } } public fun JetDeclaration.isExtensionDeclaration(): Boolean { @@ -150,82 +326,8 @@ public fun PsiElement.parameterIndex(): Int { } } -/** - * Returns enclosing qualifying element for given [[JetSimpleNameExpression]] - * ([[JetQualifiedExpression]] or [[JetUserType]] or original expression) - */ -public fun JetSimpleNameExpression.getQualifiedElement(): JetElement { - val baseExpression: JetElement = (getParent() as? JetCallExpression) ?: this - val parent = baseExpression.getParent() - return when (parent) { - is JetQualifiedExpression -> if (parent.getSelectorExpression().isAncestor(baseExpression)) parent else baseExpression - is JetUserType -> if (parent.getReferenceExpression().isAncestor(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 JetModifierListOwner.isPrivate(): Boolean = hasModifier(JetTokens.PRIVATE_KEYWORD) -public fun JetSimpleNameExpression.getReceiverExpression(): JetExpression? { - val parent = getParent() - when { - parent is JetQualifiedExpression && !isImportDirectiveExpression() -> { - 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 JetSimpleNameExpression.isImportDirectiveExpression(): Boolean { val parent = getParent() if (parent == null) { @@ -250,9 +352,6 @@ public fun JetExpression.getAssignmentByLHS(): JetBinaryExpression? { return if (JetPsiUtil.isAssignment(parent) && parent.getLeft() == this) parent else null } -public fun JetExpression.isDotReceiver(): Boolean = - (getParent() as? JetDotQualifiedExpression)?.getReceiverExpression() == this - public fun JetStringTemplateExpression.getContentRange(): TextRange { val start = getNode().getFirstChildNode().getTextLength() val lastChild = getNode().getLastChildNode() @@ -274,89 +373,3 @@ public fun JetNamedDeclaration.getValueParameterList(): JetParameterList? { else -> null } } - -// 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)) } -} - -public fun JetAnnotationsContainer.collectAnnotationEntriesFromStubOrPsi(): List = - when (this) { - is StubBasedPsiElementBase<*> -> getStub()?.collectAnnotationEntriesFromStubElement() ?: collectAnnotationEntriesFromPsi() - else -> collectAnnotationEntriesFromPsi() - } - -private fun StubElement<*>.collectAnnotationEntriesFromStubElement() = - 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() = - getChildren().flatMap { - child -> - when (child) { - is JetAnnotationEntry -> listOf(child) - is JetAnnotation -> child.getEntries() - else -> emptyList() - } - } - -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) -} - -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) -} - -// 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 JetElement.getQualifiedExpressionForSelector(): JetQualifiedExpression? { - val parent = getParent() - return if (parent is JetQualifiedExpression && parent.getSelectorExpression() == this) parent else null -} - -public fun JetElement.getQualifiedExpressionForSelectorOrThis(): JetElement { - return getQualifiedExpressionForSelector() ?: this -} \ No newline at end of file