Skip to content

Commit

Permalink
[FIR] Implement light tree CONSTRUCTOR_DELEGATION_CALL strategy
Browse files Browse the repository at this point in the history
Default strategy sometimes delegates to the CONSTRUCTOR_DELEGATION_CALL
strategy, so we add it in this commit to process some related cases
properly.
  • Loading branch information
mglukhikh committed Nov 26, 2020
1 parent 8320a29 commit d844b33
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ object FirErrors {
val NON_PRIVATE_CONSTRUCTOR_IN_ENUM by existing0<FirSourceElement, PsiElement>()
val NON_PRIVATE_CONSTRUCTOR_IN_SEALED by existing0<FirSourceElement, PsiElement>()
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0<FirSourceElement, PsiElement>(LightTreePositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL)
val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
val PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS by warning0<FirSourceElement, PsiElement>()
val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0<FirSourceElement, PsiElement>()
val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0<FirSourceElement, PsiElement>(LightTreePositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL)
val SEALED_CLASS_CONSTRUCTOR_CALL by error0<FirSourceElement, PsiElement>()

// Annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET

object LightTreePositioningStrategies {
Expand All @@ -20,16 +22,65 @@ object LightTreePositioningStrategies {
return markElement(target, tree)
}
}

val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
override fun mark(node: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): List<TextRange> {
when (node.tokenType) {
KtNodeTypes.SECONDARY_CONSTRUCTOR -> {
val valueParameterList = tree.findChildByType(node, KtNodeTypes.VALUE_PARAMETER_LIST) ?: return markElement(node, tree)
return markRange(
tree.findChildByType(node, KtTokens.CONSTRUCTOR_KEYWORD)!!,
tree.lastChild(valueParameterList)!!, tree
)
}
KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL -> {
val delegationReference = tree.findChildByType(node, KtNodeTypes.CONSTRUCTOR_DELEGATION_REFERENCE)
if (delegationReference != null && tree.firstChild(delegationReference) == null) {
val constructor = tree.findParentOfType(node, KtNodeTypes.SECONDARY_CONSTRUCTOR)!!
val valueParameterList = tree.findChildByType(constructor, KtNodeTypes.VALUE_PARAMETER_LIST)
?: return markElement(constructor, tree)
return markRange(
tree.findChildByType(constructor, KtTokens.CONSTRUCTOR_KEYWORD)!!,
tree.lastChild(valueParameterList)!!, tree
)
}
return markElement(delegationReference ?: node, tree)
}
else -> error("unexpected element $node")
}
}
}
}

fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? {
private fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull { it.tokenType == type }
}

fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterASTNode, type: TokenSet): LighterASTNode? {
private fun FlyweightCapableTreeStructure<LighterASTNode>.findChildByType(node: LighterASTNode, type: TokenSet): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull { it.tokenType in type }
}
}

private fun FlyweightCapableTreeStructure<LighterASTNode>.findParentOfType(node: LighterASTNode, type: IElementType): LighterASTNode? {
var parent = getParent(node)
while (parent != null) {
if (parent.tokenType == type) return parent
parent = getParent(parent)
}
return null
}

private fun FlyweightCapableTreeStructure<LighterASTNode>.firstChild(node: LighterASTNode): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull()
}

private fun FlyweightCapableTreeStructure<LighterASTNode>.lastChild(node: LighterASTNode): LighterASTNode? {
val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef)
return childrenRef.get()?.lastOrNull()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes

open class LightTreePositioningStrategy {
open fun markDiagnostic(diagnostic: FirDiagnostic<*>): List<TextRange> {
Expand All @@ -20,10 +21,23 @@ open class LightTreePositioningStrategy {
}

companion object {
val DEFAULT = LightTreePositioningStrategy()
val DEFAULT = object : LightTreePositioningStrategy() {
override fun mark(node: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): List<TextRange> {
when (node.tokenType) {
KtNodeTypes.CONSTRUCTOR_DELEGATION_CALL -> {
return LightTreePositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(node, tree)
}
}
return super.mark(node, tree)
}
}
}
}

fun markElement(node: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): List<TextRange> {
return listOf(TextRange(tree.getStartOffset(node), tree.getEndOffset(node)))
}

fun markRange(from: LighterASTNode, to: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): List<TextRange> {
return listOf(TextRange(tree.getStartOffset(from), tree.getEndOffset(to)))
}
36 changes: 34 additions & 2 deletions compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSourceElement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ package org.jetbrains.kotlin.fir
import com.intellij.lang.LighterASTNode
import com.intellij.lang.TreeBackedLighterAST
import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure

Expand Down Expand Up @@ -206,9 +208,39 @@ sealed class FirPsiSourceElement<out P : PsiElement>(val psi: P) : FirSourceElem
override fun disposeChildren(p0: Array<out LighterASTNode>?, p1: Int) {
}

override fun getStartOffset(node: LighterASTNode): Int = node.unwrap().startOffset
override fun getStartOffset(node: LighterASTNode): Int {
return getStartOffset(node.unwrap().psi)
}

private fun getStartOffset(element: PsiElement): Int {
var child = element.firstChild
if (child != null) {
while (child is PsiComment || child is PsiWhiteSpace) {
child = child.nextSibling
}
if (child != null) {
return getStartOffset(child)
}
}
return element.textRange.startOffset
}

override fun getEndOffset(node: LighterASTNode): Int = node.unwrap().let { it.startOffset + it.textLength }
override fun getEndOffset(node: LighterASTNode): Int {
return getEndOffset(node.unwrap().psi)
}

private fun getEndOffset(element: PsiElement): Int {
var child = element.lastChild
if (child != null) {
while (child is PsiComment || child is PsiWhiteSpace) {
child = child.prevSibling
}
if (child != null) {
return getEndOffset(child)
}
}
return element.textRange.endOffset
}
}
}

Expand Down

0 comments on commit d844b33

Please sign in to comment.