diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt
index 7a9280bbdc7..315549792bc 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticFactory.kt
@@ -23,10 +23,6 @@ sealed class AbstractFirDiagnosticFactory(
override fun toString(): String {
return name
}
-
- protected fun checkMyDiagnostic(diagnostic: FirDiagnostic) {
- require(diagnostic.factory === this) { "$diagnostic should be of this factory" }
- }
}
class FirDiagnosticFactory0(
@@ -83,11 +79,6 @@ class FirDiagnosticFactory1(
else -> incorrectElement(element)
}
}
-
- fun extractArgument(diagnostic: FirDiagnostic): A {
- checkMyDiagnostic(diagnostic)
- return (diagnostic as FirDiagnosticWithParameters1).a
- }
}
class FirDiagnosticFactory2(
@@ -124,12 +115,6 @@ class FirDiagnosticFactory2(
else -> incorrectElement(element)
}
}
-
- fun extractArguments(diagnostic: FirDiagnostic): Pair {
- checkMyDiagnostic(diagnostic)
- val typed = diagnostic as FirDiagnosticWithParameters2
- return Pair(typed.a, typed.b)
- }
}
class FirDiagnosticFactory3(
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 3efdda344d2..b15d769d630 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
@@ -833,6 +833,33 @@ object LightTreePositioningStrategies {
private fun lastSymbol(range: TextRange): TextRange =
if (range.isEmpty) range else TextRange.create(range.endOffset - 1, range.endOffset)
}
+
+ val UNREACHABLE_CODE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() {
+ override fun markFirDiagnostic(element: FirSourceElement, diagnostic: FirDiagnostic): List {
+ @Suppress("UNCHECKED_CAST")
+ val typed = diagnostic as FirDiagnosticWithParameters2, Set>
+ with(UnreachableCodeLightTreeHelper(element.treeStructure)) {
+ val reachable = typed.a.map { it.lighterASTNode }.toSet()
+ val unreachable = typed.b.map { it.lighterASTNode }.toSet()
+ if (!element.lighterASTNode.hasChildrenInSet(reachable)) {
+ return super.markFirDiagnostic(element, diagnostic)
+ }
+
+ val nodesToMark = element.lighterASTNode.getLeavesOrReachableChildren(reachable, unreachable)
+ .removeReachableElementsWithMeaninglessSiblings(reachable)
+
+ if (nodesToMark.isEmpty()) {
+ return super.markFirDiagnostic(element, diagnostic)
+ }
+
+ val ranges = nodesToMark.flatMap {
+ markElement(it, element.startOffset, element.endOffset, element.treeStructure, element.lighterASTNode)
+ }
+
+ return ranges.mergeAdjacentTextRanges()
+ }
+ }
+ }
}
fun FirSourceElement.hasValOrVar(): Boolean =
@@ -1100,6 +1127,29 @@ fun FlyweightCapableTreeStructure.collectDescendantsOfType(
return result
}
+fun FlyweightCapableTreeStructure.traverseDescendants(
+ node: LighterASTNode,
+ acceptor: (LighterASTNode) -> Boolean
+) {
+ fun FlyweightCapableTreeStructure.traverse(node: LighterASTNode) {
+ val childrenRef = Ref>()
+ getChildren(node, childrenRef)
+
+ val childrenRefGet = childrenRef.get()
+ if (childrenRefGet != null) {
+ for (child in childrenRefGet) {
+ if (child != null) {
+ if (acceptor(child)) {
+ traverse(child)
+ }
+ }
+ }
+ }
+ }
+
+ traverse(node)
+}
+
fun FlyweightCapableTreeStructure.findChildByType(node: LighterASTNode, type: TokenSet): LighterASTNode? {
val childrenRef = Ref>()
getChildren(node, childrenRef)
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 fefc38681ec..4b8ab630c0f 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
@@ -88,11 +88,12 @@ private val FILLER_TOKENS = setOf(
)
private fun LighterASTNode.nonFillerFirstChildOrSelf(tree: FlyweightCapableTreeStructure): LighterASTNode =
- getChildren(tree).firstOrNull { it != null && it.tokenType !in FILLER_TOKENS } ?: this
+ getChildren(tree).firstOrNull { it != null && !it.isFiller() } ?: this
internal fun LighterASTNode.nonFillerLastChildOrSelf(tree: FlyweightCapableTreeStructure): LighterASTNode =
- getChildren(tree).lastOrNull { it != null && it.tokenType !in FILLER_TOKENS } ?: this
+ getChildren(tree).lastOrNull { it != null && !it.isFiller() } ?: this
+internal fun LighterASTNode.isFiller() = tokenType in FILLER_TOKENS
private fun hasSyntaxErrors(node: LighterASTNode, tree: FlyweightCapableTreeStructure): Boolean {
if (node.tokenType == TokenType.ERROR_ELEMENT) return true
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt
index ddb132f0104..77688447397 100644
--- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt
@@ -274,7 +274,7 @@ object SourceElementPositioningStrategies {
)
val UNREACHABLE_CODE = SourceElementPositioningStrategy(
- LightTreePositioningStrategies.DEFAULT, //todo
+ LightTreePositioningStrategies.UNREACHABLE_CODE,
FirPsiPositioningStrategies.UNREACHABLE_CODE
)
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/UnreachableCodeLightTreeHelper.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/UnreachableCodeLightTreeHelper.kt
new file mode 100644
index 00000000000..01e1d0526e9
--- /dev/null
+++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/UnreachableCodeLightTreeHelper.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+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.fir.analysis.checkers.getChildren
+import org.jetbrains.kotlin.lexer.KtTokens
+
+typealias Node = LighterASTNode
+
+class UnreachableCodeLightTreeHelper(val tree: FlyweightCapableTreeStructure) {
+
+ fun Node.hasChildrenInSet(set: Set): Boolean {
+ var result = false
+ tree.traverseDescendants(this) {
+ if (!result && it != this && it in set) {
+ result = true
+ }
+ !result
+ }
+ return result
+ }
+
+ fun Node.getLeavesOrReachableChildren(reachable: Set, unreachable: Set): List {
+ val result = mutableListOf()
+ tree.traverseDescendants(this) { element ->
+ val isReachable = element in reachable && !element.hasChildrenInSet(unreachable)
+ if (isReachable || element.getChildren(tree).isEmpty()) {
+ result.add(element)
+ false
+ } else {
+ true
+ }
+ }
+ return result
+ }
+
+ fun List.removeReachableElementsWithMeaninglessSiblings(reachableElements: Set): List {
+ val childrenToRemove = mutableSetOf()
+ fun collectSiblingsIfMeaningless(elementIndex: Int, direction: Int) {
+ val index = elementIndex + direction
+ if (index !in 0 until size) return
+
+ val element = this[index]
+ if (element.isFiller() || element.tokenType == KtTokens.COMMA) {
+ childrenToRemove.add(element)
+ collectSiblingsIfMeaningless(index, direction)
+ }
+ }
+ for ((index, element) in this.withIndex()) {
+ if (reachableElements.contains(element)) {
+ childrenToRemove.add(element)
+ collectSiblingsIfMeaningless(index, -1)
+ collectSiblingsIfMeaningless(index, 1)
+ }
+ }
+ return filter { it !in childrenToRemove }
+ }
+
+ fun List.mergeAdjacentTextRanges(): List {
+ val result = ArrayList()
+ val lastRange = fold(null as TextRange?) { currentTextRange, elementRange ->
+ when {
+ currentTextRange == null -> {
+ elementRange
+ }
+ currentTextRange.endOffset == elementRange.startOffset -> {
+ currentTextRange.union(elementRange)
+ }
+ else -> {
+ result.add(currentTextRange)
+ elementRange
+ }
+ }
+ }
+ if (lastRange != null) {
+ result.add(lastRange)
+ }
+ return result
+ }
+}
\ No newline at end of file