[FIR] Implement light tree CONSTRUCTOR_DELEGATION_CALL strategy

Default strategy sometimes delegates to the CONSTRUCTOR_DELEGATION_CALL
strategy, so we add it in this commit to process some related cases
properly.
This commit is contained in:
Mikhail Glukhikh
2020-11-17 15:06:01 +03:00
parent 8320a2966a
commit d844b33b1c
4 changed files with 105 additions and 8 deletions
@@ -71,11 +71,11 @@ object FirErrors {
val NON_PRIVATE_CONSTRUCTOR_IN_ENUM by existing0<FirSourceElement, PsiElement>() val NON_PRIVATE_CONSTRUCTOR_IN_ENUM by existing0<FirSourceElement, PsiElement>()
val NON_PRIVATE_CONSTRUCTOR_IN_SEALED by existing0<FirSourceElement, PsiElement>() val NON_PRIVATE_CONSTRUCTOR_IN_SEALED by existing0<FirSourceElement, PsiElement>()
val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0<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 SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0<FirSourceElement, PsiElement>()
val DELEGATION_SUPER_CALL_IN_ENUM_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 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>() val SEALED_CLASS_CONSTRUCTOR_CALL by error0<FirSourceElement, PsiElement>()
// Annotations // Annotations
@@ -11,6 +11,8 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet import com.intellij.psi.tree.TokenSet
import com.intellij.util.diff.FlyweightCapableTreeStructure 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 import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
object LightTreePositioningStrategies { object LightTreePositioningStrategies {
@@ -20,16 +22,65 @@ object LightTreePositioningStrategies {
return markElement(target, tree) 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>>() val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef) getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull { it.tokenType == type } 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>>() val childrenRef = Ref<Array<LighterASTNode>>()
getChildren(node, childrenRef) getChildren(node, childrenRef)
return childrenRef.get()?.firstOrNull { it.tokenType in type } 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()
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.KtNodeTypes
open class LightTreePositioningStrategy { open class LightTreePositioningStrategy {
open fun markDiagnostic(diagnostic: FirDiagnostic<*>): List<TextRange> { open fun markDiagnostic(diagnostic: FirDiagnostic<*>): List<TextRange> {
@@ -20,10 +21,23 @@ open class LightTreePositioningStrategy {
} }
companion object { 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> { fun markElement(node: LighterASTNode, tree: FlyweightCapableTreeStructure<LighterASTNode>): List<TextRange> {
return listOf(TextRange(tree.getStartOffset(node), tree.getEndOffset(node))) 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)))
}
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.fir
import com.intellij.lang.LighterASTNode import com.intellij.lang.LighterASTNode
import com.intellij.lang.TreeBackedLighterAST import com.intellij.lang.TreeBackedLighterAST
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
@@ -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 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)
}
override fun getEndOffset(node: LighterASTNode): Int = node.unwrap().let { it.startOffset + it.textLength } 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 {
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
}
} }
} }