Drop UNREACHABLE_CODE eager text range calculation
#KT-35242 Fixed
This commit is contained in:
@@ -281,7 +281,7 @@ class ControlFlowInformationProvider private constructor(
|
||||
|
||||
private fun reportUnreachableCode(unreachableCode: UnreachableCode) {
|
||||
for (element in unreachableCode.elements) {
|
||||
trace.report(Errors.UNREACHABLE_CODE.on(element, unreachableCode.getUnreachableTextRanges(element)))
|
||||
trace.report(Errors.UNREACHABLE_CODE.on(element, unreachableCode.reachableElements, unreachableCode.unreachableElements))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,97 +29,112 @@ import java.util.*
|
||||
|
||||
interface UnreachableCode {
|
||||
val elements: Set<KtElement>
|
||||
fun getUnreachableTextRanges(element: KtElement): List<TextRange>
|
||||
val reachableElements: Set<KtElement>
|
||||
val unreachableElements: Set<KtElement>
|
||||
|
||||
companion object {
|
||||
fun getUnreachableTextRanges(
|
||||
element: KtElement,
|
||||
reachableElements: Set<KtElement>,
|
||||
unreachableElements: Set<KtElement>
|
||||
): List<TextRange> {
|
||||
return if (element.hasChildrenInSet(reachableElements)) {
|
||||
with(
|
||||
element.getLeavesOrReachableChildren(reachableElements, unreachableElements)
|
||||
.removeReachableElementsWithMeaninglessSiblings(reachableElements).mergeAdjacentTextRanges()
|
||||
) {
|
||||
if (isNotEmpty()) this
|
||||
// Specific case like condition in when:
|
||||
// element is dead but its only child is alive and has the same text range
|
||||
else listOf(element.textRange.endOffset.let { TextRange(it, it) })
|
||||
}
|
||||
} else {
|
||||
listOf(element.textRange!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtElement.hasChildrenInSet(set: Set<KtElement>): Boolean =
|
||||
PsiTreeUtil.collectElements(this) { it != this }.any { it in set }
|
||||
|
||||
private fun KtElement.getLeavesOrReachableChildren(
|
||||
reachableElements: Set<KtElement>,
|
||||
unreachableElements: Set<KtElement>
|
||||
): List<PsiElement> {
|
||||
val children = ArrayList<PsiElement>()
|
||||
acceptChildren(object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
val isReachable =
|
||||
element is KtElement && reachableElements.contains(element) && !element.hasChildrenInSet(unreachableElements)
|
||||
if (isReachable || element.children.isEmpty()) {
|
||||
children.add(element)
|
||||
} else {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
})
|
||||
return children
|
||||
}
|
||||
|
||||
private fun List<PsiElement>.removeReachableElementsWithMeaninglessSiblings(reachableElements: Set<KtElement>): List<PsiElement> {
|
||||
fun PsiElement.isMeaningless() = this is PsiWhiteSpace
|
||||
|| this.node?.elementType == KtTokens.COMMA
|
||||
|| this is PsiComment
|
||||
|
||||
val childrenToRemove = HashSet<PsiElement>()
|
||||
fun collectSiblingsIfMeaningless(elementIndex: Int, direction: Int) {
|
||||
val index = elementIndex + direction
|
||||
if (index !in 0 until size) return
|
||||
|
||||
val element = this[index]
|
||||
if (element.isMeaningless()) {
|
||||
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 this.filter { it !in childrenToRemove }
|
||||
}
|
||||
|
||||
|
||||
private fun List<PsiElement>.mergeAdjacentTextRanges(): List<TextRange> {
|
||||
val result = ArrayList<TextRange>()
|
||||
val lastRange = fold(null as TextRange?) { currentTextRange, element ->
|
||||
|
||||
val elementRange = element.textRange!!
|
||||
when {
|
||||
currentTextRange == null -> {
|
||||
elementRange
|
||||
}
|
||||
currentTextRange.endOffset == elementRange.startOffset -> {
|
||||
currentTextRange.union(elementRange)
|
||||
}
|
||||
else -> {
|
||||
result.add(currentTextRange)
|
||||
elementRange
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastRange != null) {
|
||||
result.add(lastRange)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UnreachableCodeImpl(
|
||||
private val reachableElements: Set<KtElement>,
|
||||
private val unreachableElements: Set<KtElement>
|
||||
override val reachableElements: Set<KtElement>,
|
||||
override val unreachableElements: Set<KtElement>
|
||||
) : UnreachableCode {
|
||||
|
||||
// This is needed in order to highlight only '1 < 2' and not '1', '<' and '2' as well
|
||||
override val elements: Set<KtElement> = KtPsiUtil.findRootExpressions(unreachableElements)
|
||||
|
||||
override fun getUnreachableTextRanges(element: KtElement): List<TextRange> {
|
||||
return if (element.hasChildrenInSet(reachableElements)) {
|
||||
with(element.getLeavesOrReachableChildren().removeReachableElementsWithMeaninglessSiblings().mergeAdjacentTextRanges()) {
|
||||
if (isNotEmpty()) this
|
||||
// Specific case like condition in when:
|
||||
// element is dead but its only child is alive and has the same text range
|
||||
else listOf(element.textRange.endOffset.let { TextRange(it, it) })
|
||||
}
|
||||
} else {
|
||||
listOf(element.textRange!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtElement.hasChildrenInSet(set: Set<KtElement>): Boolean =
|
||||
PsiTreeUtil.collectElements(this) { it != this }.any { it in set }
|
||||
|
||||
private fun KtElement.getLeavesOrReachableChildren(): List<PsiElement> {
|
||||
val children = ArrayList<PsiElement>()
|
||||
acceptChildren(object : PsiElementVisitor() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
val isReachable =
|
||||
element is KtElement && reachableElements.contains(element) && !element.hasChildrenInSet(unreachableElements)
|
||||
if (isReachable || element.children.isEmpty()) {
|
||||
children.add(element)
|
||||
} else {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
})
|
||||
return children
|
||||
}
|
||||
|
||||
private fun List<PsiElement>.removeReachableElementsWithMeaninglessSiblings(): List<PsiElement> {
|
||||
fun PsiElement.isMeaningless() = this is PsiWhiteSpace
|
||||
|| this.node?.elementType == KtTokens.COMMA
|
||||
|| this is PsiComment
|
||||
|
||||
val childrenToRemove = HashSet<PsiElement>()
|
||||
fun collectSiblingsIfMeaningless(elementIndex: Int, direction: Int) {
|
||||
val index = elementIndex + direction
|
||||
if (index !in 0..(size - 1)) return
|
||||
|
||||
val element = this[index]
|
||||
if (element.isMeaningless()) {
|
||||
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 this.filter { it !in childrenToRemove }
|
||||
}
|
||||
|
||||
|
||||
private fun List<PsiElement>.mergeAdjacentTextRanges(): List<TextRange> {
|
||||
val result = ArrayList<TextRange>()
|
||||
val lastRange = fold(null as TextRange?) { currentTextRange, element ->
|
||||
|
||||
val elementRange = element.textRange!!
|
||||
when {
|
||||
currentTextRange == null -> {
|
||||
elementRange
|
||||
}
|
||||
currentTextRange.endOffset == elementRange.startOffset -> {
|
||||
currentTextRange.union(elementRange)
|
||||
}
|
||||
else -> {
|
||||
result.add(currentTextRange)
|
||||
elementRange
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastRange != null) {
|
||||
result.add(lastRange)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.diagnostics;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import kotlin.Pair;
|
||||
@@ -37,6 +36,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.PositioningStrategies.*;
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.*;
|
||||
@@ -886,7 +886,7 @@ public interface Errors {
|
||||
|
||||
// Control flow / Data flow
|
||||
|
||||
DiagnosticFactory1<KtElement, List<TextRange>> UNREACHABLE_CODE = DiagnosticFactory1.create(
|
||||
DiagnosticFactory2<KtElement, Set<KtElement>, Set<KtElement>> UNREACHABLE_CODE = DiagnosticFactory2.create(
|
||||
WARNING, PositioningStrategies.UNREACHABLE_CODE);
|
||||
|
||||
DiagnosticFactory0<KtVariableDeclaration> VARIABLE_WITH_NO_TYPE_NO_INITIALIZER = DiagnosticFactory0.create(ERROR, DECLARATION_NAME);
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import org.jetbrains.kotlin.KtNodeTypes
|
||||
import org.jetbrains.kotlin.cfg.UnreachableCode
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.ACTUAL_WITHOUT_EXPECT
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.NO_ACTUAL_FOR_EXPECT
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
@@ -579,7 +580,8 @@ object PositioningStrategies {
|
||||
@JvmField
|
||||
val UNREACHABLE_CODE: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
|
||||
override fun markDiagnostic(diagnostic: ParametrizedDiagnostic<out PsiElement>): List<TextRange> {
|
||||
return Errors.UNREACHABLE_CODE.cast(diagnostic).a
|
||||
val unreachableCode = Errors.UNREACHABLE_CODE.cast(diagnostic)
|
||||
return UnreachableCode.getUnreachableTextRanges(unreachableCode.psiElement, unreachableCode.a, unreachableCode.b)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -351,7 +351,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(VAL_OR_VAR_ON_CATCH_PARAMETER, "''{0}'' on catch parameter is not allowed", TO_STRING);
|
||||
MAP.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING);
|
||||
|
||||
MAP.put(UNREACHABLE_CODE, "Unreachable code", TO_STRING);
|
||||
MAP.put(UNREACHABLE_CODE, "Unreachable code", EMPTY, EMPTY);
|
||||
|
||||
MAP.put(MANY_COMPANION_OBJECTS, "Only one companion object is allowed per class");
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ object Renderers {
|
||||
element.toString()
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val EMPTY = Renderer<Any> { "" }
|
||||
|
||||
@JvmField
|
||||
val STRING = Renderer<String> { it }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user