diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index f2756bf2506..4303a48e8eb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -71,11 +71,11 @@ object FirErrors { val NON_PRIVATE_CONSTRUCTOR_IN_ENUM by existing0() val NON_PRIVATE_CONSTRUCTOR_IN_SEALED by existing0() val CYCLIC_CONSTRUCTOR_DELEGATION_CALL by warning0() - val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0() + val PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED by warning0(LightTreePositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) val SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR by warning0() val DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR by warning0() val PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS by warning0() - val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0() + val EXPLICIT_DELEGATION_CALL_REQUIRED by warning0(LightTreePositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL) val SEALED_CLASS_CONSTRUCTOR_CALL by error0() // Annotations diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index fe159417118..63353c5d268 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -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 { @@ -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): List { + 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.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? { +private fun FlyweightCapableTreeStructure.findChildByType(node: LighterASTNode, type: IElementType): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) return childrenRef.get()?.firstOrNull { it.tokenType == type } } -fun FlyweightCapableTreeStructure.findChildByType(node: LighterASTNode, type: TokenSet): LighterASTNode? { +private fun FlyweightCapableTreeStructure.findChildByType(node: LighterASTNode, type: TokenSet): LighterASTNode? { val childrenRef = Ref>() getChildren(node, childrenRef) return childrenRef.get()?.firstOrNull { it.tokenType in type } -} \ No newline at end of file +} + +private fun FlyweightCapableTreeStructure.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.firstChild(node: LighterASTNode): LighterASTNode? { + val childrenRef = Ref>() + getChildren(node, childrenRef) + return childrenRef.get()?.firstOrNull() +} + +private fun FlyweightCapableTreeStructure.lastChild(node: LighterASTNode): LighterASTNode? { + val childrenRef = Ref>() + getChildren(node, childrenRef) + return childrenRef.get()?.lastOrNull() +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategy.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategy.kt index 9c3206c5c24..1b11a5051cd 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategy.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategy.kt @@ -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 { @@ -20,10 +21,23 @@ open class LightTreePositioningStrategy { } companion object { - val DEFAULT = LightTreePositioningStrategy() + val DEFAULT = object : LightTreePositioningStrategy() { + override fun mark(node: LighterASTNode, tree: FlyweightCapableTreeStructure): List { + 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): List { return listOf(TextRange(tree.getStartOffset(node), tree.getEndOffset(node))) } + +fun markRange(from: LighterASTNode, to: LighterASTNode, tree: FlyweightCapableTreeStructure): List { + return listOf(TextRange(tree.getStartOffset(from), tree.getEndOffset(to))) +} diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSourceElement.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSourceElement.kt index 39f672433b3..64951b8468d 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSourceElement.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirSourceElement.kt @@ -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 @@ -206,9 +208,39 @@ sealed class FirPsiSourceElement(val psi: P) : FirSourceElem override fun disposeChildren(p0: Array?, 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 + } } }