diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index cecf55d8010..d816f26ba57 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction @@ -943,6 +944,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val INITIALIZATION_BEFORE_DECLARATION by error() { parameter("property") } + + val UNREACHABLE_CODE by warning(PositioningStrategy.UNREACHABLE_CODE) { + parameter>("reachable") + parameter>("unreachable") + } } val NULLABILITY by object : DiagnosticGroup("Nullability") { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt index 000b36744da..df4323d56b9 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/RegularDiagnosticData.kt @@ -97,6 +97,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { DECLARATION_WITH_BODY, INCOMPATIBLE_DECLARATION, ACTUAL_DECLARATION_NAME, + UNREACHABLE_CODE, ; diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 100180289d3..10e01c3a76e 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.diagnostics.SourceElementPositioningStrategies import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.expressions.FirExpression @@ -505,6 +506,7 @@ object FirErrors { val WRONG_IMPLIES_CONDITION by warning0() val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0(SourceElementPositioningStrategies.DECLARATION_NAME) val INITIALIZATION_BEFORE_DECLARATION by error1>() + val UNREACHABLE_CODE by warning2, Set>(SourceElementPositioningStrategies.UNREACHABLE_CODE) // Nullability val UNSAFE_CALL by error2(SourceElementPositioningStrategies.DOT_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ExtendedDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ExtendedDeclarationCheckers.kt index 796c502e3fa..d20d2129f59 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ExtendedDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/ExtendedDeclarationCheckers.kt @@ -30,6 +30,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() { override val controlFlowAnalyserCheckers: Set get() = setOf( UnusedChecker, + UnreachableCodeChecker, ) override val simpleFunctionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnreachableCodeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnreachableCodeChecker.kt new file mode 100644 index 00000000000..27dbdbc6679 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnreachableCodeChecker.kt @@ -0,0 +1,81 @@ +/* + * 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.checkers.extended + +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.expressions.FirTryExpression +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid + +object UnreachableCodeChecker : FirControlFlowChecker() { + + override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter, context: CheckerContext) { + val nodes = graph.allNodes() + val (unreachableNodes, reachableNodes) = nodes.filterNot { it.skipNode() }.partition { it.isDead } + if (unreachableNodes.isEmpty()) return + val unreachableSources = unreachableNodes.mapNotNull { it.fir.source }.toSet() + val reachableSources = reachableNodes.mapNotNull { it.fir.source }.toSet() + val unreachableElements = unreachableNodes.map { it.fir } + val innerNodes = mutableSetOf() + unreachableElements.forEach { it.collectInnerNodes(innerNodes) } + //todo cfg is broken in catch and finally blocks, so exclude reporting anything + nodes.mapNotNull { it.fir as? FirTryExpression }.distinct().forEach { tryNode -> + tryNode.finallyBlock?.collectInnerNodes(innerNodes) + tryNode.catches.forEach { it.collectInnerNodes(innerNodes) } + } + unreachableElements.distinctBy { it.source }.forEach { element -> + if (element !in innerNodes) { + reporter.reportOn(element.source, FirErrors.UNREACHABLE_CODE, reachableSources, unreachableSources, context) + } + } + } + + private fun ControlFlowGraph.allNodes(acc: MutableList> = mutableListOf()): List> { + acc.addAll(this.nodes) + subGraphs.forEach { it.allNodes(acc) } + return acc + } + + private val sourceKindsToSkip = setOf( + FirFakeSourceElementKind.ImplicitReturn, + FirFakeSourceElementKind.DesugaredForLoop + ) + + private fun CFGNode<*>.skipNode(): Boolean { + val skipType = this is ExitNodeMarker || + this is EnterNodeMarker || + this is StubNode || + this is BinaryOrExitLeftOperandNode || + this is BinaryOrEnterRightOperandNode || + this is BinaryAndExitLeftOperandNode || + this is BinaryAndEnterRightOperandNode || + this is WhenSyntheticElseBranchNode || + this is WhenBranchResultEnterNode || + this is WhenBranchResultExitNode + val allowType = this is LoopEnterNode || + this is LoopBlockEnterNode || + this is TryExpressionEnterNode + return !allowType && (skipType || sourceKindsToSkip.contains(this.fir.source?.kind)) + } + + + private fun FirElement.collectInnerNodes(nodes: MutableSet) { + acceptChildren(CollectNodesVisitor(nodes)) + } + + private class CollectNodesVisitor(private val nodes: MutableSet) : FirVisitorVoid() { + override fun visitElement(element: FirElement) { + nodes.add(element) + element.acceptChildren(this) + } + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt index 87d3c59d489..d7626eaf69e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/UnusedChecker.kt @@ -67,6 +67,7 @@ object UnusedChecker : FirControlFlowChecker() { override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { val variableSymbol = node.fir.symbol + if (node.fir.source == null) return if (variableSymbol.isLoopIterator) return val dataPerNode = data[node] ?: return for (dataPerLabel in dataPerNode.values) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 30c8209031d..b80515f9112 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -397,6 +397,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNINITIALIZED_VAR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_LATEINIT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_SAFE_CALL +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNREACHABLE_CODE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_LABEL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_CALL @@ -1283,6 +1284,7 @@ class FirDefaultErrorMessages { ) map.put(LEAKED_IN_PLACE_LAMBDA, "Leaked in-place lambda: {2}", SYMBOL) map.put(FirErrors.WRONG_IMPLIES_CONDITION, "Wrong implies condition") + map.put(UNREACHABLE_CODE, "Unreachable code", NOT_RENDERED, NOT_RENDERED) // Nullability map.put( 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 315549792bc..7a9280bbdc7 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,6 +23,10 @@ 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( @@ -79,6 +83,11 @@ class FirDiagnosticFactory1( else -> incorrectElement(element) } } + + fun extractArgument(diagnostic: FirDiagnostic): A { + checkMyDiagnostic(diagnostic) + return (diagnostic as FirDiagnosticWithParameters1).a + } } class FirDiagnosticFactory2( @@ -115,6 +124,12 @@ 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/FirPsiPositioningStrategy.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPsiPositioningStrategy.kt new file mode 100644 index 00000000000..4d483dff851 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirPsiPositioningStrategy.kt @@ -0,0 +1,36 @@ +/* + * 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.openapi.util.TextRange +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.cfg.UnreachableCode +import org.jetbrains.kotlin.diagnostics.PositioningStrategy +import org.jetbrains.kotlin.fir.FirPsiSourceElement +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.psi.KtElement + +abstract class FirPsiPositioningStrategy : PositioningStrategy(), + FirDiagnosticPositioningStrategy + +object FirPsiPositioningStrategies { + + val UNREACHABLE_CODE = object : FirPsiPositioningStrategy() { + + override fun markFirDiagnostic(element: FirPsiSourceElement, diagnostic: FirDiagnostic): List { + //todo it is better to implement arguments extraction in FirDiagnosticFactory, but kotlin struggle with checking types in it atm + @Suppress("UNCHECKED_CAST") + val typed = diagnostic as FirDiagnosticWithParameters2, Set> + return UnreachableCode.getUnreachableTextRanges( + element.psi as KtElement, + typed.a.mapNotNull { it.psi as? KtElement }.toSet(), + typed.b.mapNotNull { it.psi as? KtElement }.toSet() + ) + } + } + +} \ No newline at end of file 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 c627798717a..fefc38681ec 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 @@ -10,12 +10,22 @@ import com.intellij.openapi.util.Ref import com.intellij.openapi.util.TextRange import com.intellij.psi.TokenType import com.intellij.util.diff.FlyweightCapableTreeStructure +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE -open class LightTreePositioningStrategy { +interface FirDiagnosticPositioningStrategy { + fun markFirDiagnostic(element: E, diagnostic: FirDiagnostic): List +} + +open class LightTreePositioningStrategy : FirDiagnosticPositioningStrategy { + + override fun markFirDiagnostic(element: FirSourceElement, diagnostic: FirDiagnostic): List { + return mark(element.lighterASTNode, element.startOffset, element.endOffset, element.treeStructure) + } + open fun mark( node: LighterASTNode, startOffset: Int, 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 e619e2b21eb..ddb132f0104 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 @@ -273,6 +273,11 @@ object SourceElementPositioningStrategies { PositioningStrategies.DECLARATION_WITH_BODY ) + val UNREACHABLE_CODE = SourceElementPositioningStrategy( + LightTreePositioningStrategies.DEFAULT, //todo + FirPsiPositioningStrategies.UNREACHABLE_CODE + ) + // TODO val ACTUAL_DECLARATION_NAME = DEFAULT val INCOMPATIBLE_DECLARATION = DEFAULT diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategy.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategy.kt index 6448c8f2e6a..7a9ae0f11be 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategy.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategy.kt @@ -18,10 +18,14 @@ class SourceElementPositioningStrategy( fun markDiagnostic(diagnostic: FirDiagnostic): List { val element = diagnostic.element if (element is FirPsiSourceElement) { - @Suppress("UNCHECKED_CAST") - return psiStrategy.hackyMark(element.psi) + return if (psiStrategy is FirPsiPositioningStrategy<*>) { + psiStrategy.markFirDiagnostic(element, diagnostic) + } else { + @Suppress("UNCHECKED_CAST") + psiStrategy.hackyMark(element.psi) + } } - return lightTreeStrategy.mark(element.lighterASTNode, element.startOffset, element.endOffset, element.treeStructure) + return lightTreeStrategy.markFirDiagnostic(element, diagnostic) } fun isValid(element: FirSourceElement): Boolean { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.fir.kt deleted file mode 100644 index 7cd5ee47a01..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -fun testCommasAndWhitespaces() { - fun bar(i: Int, s: String, x: Any) {} - - bar( 1 , todo() , "" ) -} - -fun todo(): Nothing = throw Exception() - - - diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.kt index b184b4cd25a..ac45ba9d241 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commasAndWhitespaces.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER fun testCommasAndWhitespaces() { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commentsInDeadCode.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commentsInDeadCode.fir.kt index 1eb8bff5c0a..7e3230febec 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commentsInDeadCode.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/commentsInDeadCode.fir.kt @@ -1,18 +1,18 @@ package a fun test1() { - bar( + bar( 11, todo(),//comment1 - ""//comment2 - ) + ""//comment2 + ) } fun test2() { - bar(11, todo()/*comment1*/, ""/*comment2*/) + bar(11, todo()/*comment1*/, ""/*comment2*/) } fun test3() { - bar(11, l@(todo()/*comment*/), "") + bar(11, l@(todo()/*comment*/), "") } fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInInvokeCall.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInInvokeCall.fir.kt index a0c9af7bfbe..446699c7a78 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInInvokeCall.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInInvokeCall.fir.kt @@ -1,11 +1,11 @@ fun testInvoke() { operator fun Nothing.invoke(): Nothing = this - todo()() + todo()() } fun testInvokeWithLambda() { operator fun Nothing.invoke(i: Int, f: () -> Int) = f - todo()(1){ 42 } + todo()(1){ 42 } } -fun todo(): Nothing = throw Exception() \ No newline at end of file +fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt index 4ed01f752af..a2290e8bfea 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCallInReceiver.fir.kt @@ -1,11 +1,11 @@ fun test11() { fun Any.bar(i: Int) {} - todo().bar(1) + todo().bar(1) } fun test12() { fun Any.bar(i: Int) {} - todo()?.bar(1) + todo()?.bar(1) } fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt index f82af44ce90..409175145b3 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt @@ -3,80 +3,80 @@ fun t1() : Int{ return 0 - 1 + 1 } fun t1a() : Int { return - return 1 - 1 + return 1 + 1 } fun t1b() : Int { return 1 - return 1 - 1 + return 1 + 1 } fun t1c() : Int { return 1 - return - 1 + return + 1 } fun t2() : Int { if (1 > 2) return 1 else return 1 - 1 + 1 } fun t2a() : Int { if (1 > 2) { return 1 - 1 + 1 } else { return 1 - 2 + 2 } - 1 + 1 } fun t3() : Any { if (1 > 2) return 2 else return "" - 1 + 1 } fun t4(a : Boolean) : Int { do { return 1 } - while (a) - 1 + while (a) + 1 } fun t4break(a : Boolean) : Int { do { break } - while (a) + while (a) return 1 } fun t5() : Int { do { return 1 - 2 + 2 } - while (1 > 2) - return 1 + while (1 > 2) + return 1 } fun t6() : Int { while (1 > 2) { return 1 - 2 + 2 } return 1 } @@ -84,7 +84,7 @@ fun t6() : Int { fun t6break() : Int { while (1 > 2) { break - 2 + 2 } return 1 } @@ -92,7 +92,7 @@ fun t6break() : Int { fun t7(b : Int) : Int { for (i in 1..b) { return 1 - 2 + 2 } return 1 } @@ -100,7 +100,7 @@ fun t7(b : Int) : Int { fun t7break(b : Int) : Int { for (i in 1..b) { return 1 - 2 + 2 } return 1 } @@ -108,54 +108,54 @@ fun t7break(b : Int) : Int { fun t7() : Int { try { return 1 - 2 + 2 } catch (e : Any) { 2 } - return 1 // this is OK, like in Java + return 1 // this is OK, like in Java } fun t8() : Int { try { return 1 - 2 + 2 } catch (e : Any) { return 1 2 } - return 1 + return 1 } fun blockAndAndMismatch() : Boolean { - (return true) || (return false) - return true + (return true) || (return false) + return true } fun tf() : Int { try {return 1} finally{return 1} - return 1 + return 1 } fun failtest(a : Int) : Int { - if (fail() || true) { + if (fail() || true) { } - return 1 + return 1 } -fun foo(a : Nothing) : Unit { +fun foo(a : Nothing) : Unit { 1 a - 2 + 2 } fun fail() : Nothing { throw java.lang.RuntimeException() } -fun nullIsNotNothing() : Unit { +fun nullIsNotNothing() : Unit { val x : Int? = 1 if (x != null) { return @@ -165,5 +165,5 @@ fun nullIsNotNothing() : Unit { fun returnInWhile(a: Int) { do {return} - while (1 > a) + while (1 > a) } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeFromDifferentSources.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeFromDifferentSources.fir.kt index d2c13049fb0..851b3434054 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeFromDifferentSources.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeFromDifferentSources.fir.kt @@ -1,17 +1,17 @@ package c fun test1() { - val r: Nothing = null!! + val r: Nothing = null!! } fun test2(a: A) { a + a - bar() + bar() } fun test3() { null!! - bar() + bar() } fun throwNPE(): Nothing = null!! diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt index 74ab2d9781b..909c220f100 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt @@ -1,40 +1,40 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER fun testArrayAccess1(array: Array) { - array[todo()] + array[todo()] } fun testArrayAccess2() { operator fun Nothing.get(i: Int, s: String) {} - todo()[1, ""] + todo()[1, ""] } fun testAraryAccess3() { operator fun Nothing.get(n: Nothing) {} - todo()[todo()] + todo()[todo()] } fun testArrayAssignment1(array: Array) { - array[todo()] = 11 + array[todo()] = 11 } fun testArrayAssignment2(array: Array) { - array[1] = todo() + array[1] = todo() } fun testArrayAssignment3(n: Nothing) { operator fun Nothing.set(i: Int, j: Int) {} - n[1] = 2 + n[1] = 2 } fun testArrayAssignment4(n: Nothing) { operator fun Nothing.set(i: Int, a: Any) {} - n[1] = todo() + n[1] = todo() } fun testArrayPlusAssign(array: Array) { operator fun Any.plusAssign(a: Any) {} - array[1] += todo() + array[1] += todo() } -fun todo(): Nothing = throw Exception() \ No newline at end of file +fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInAssignment.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInAssignment.fir.kt index 62e8cdabb94..828377b9d92 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInAssignment.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInAssignment.fir.kt @@ -1,18 +1,18 @@ fun testAssignment() { - var a = 1 - a = todo() + var a = 1 + a = todo() } fun testVariableDeclaration() { - val a = todo() + val a = todo() } fun testPlusAssign() { operator fun Int.plusAssign(i: Int) {} - var a = 1 - a += todo() + var a = 1 + a += todo() } -fun todo(): Nothing = throw Exception() \ No newline at end of file +fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInBinaryExpressions.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInBinaryExpressions.fir.kt index 4fc51660f92..3ce6fc2366a 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInBinaryExpressions.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInBinaryExpressions.fir.kt @@ -1,14 +1,14 @@ fun testBinary1() { operator fun Int.times(s: String) {} - todo() * "" + todo() * "" } fun testBinary2() { - "1" + todo() + "1" + todo() } fun testElvis1() { - todo() ?: "" + todo() ?: "" } fun testElvis2(s: String?) { @@ -24,15 +24,15 @@ fun testAnd1(b: Boolean) { } fun testAnd2(b: Boolean) { - todo() && b + todo() && b } fun returnInBinary1(): Boolean { - (return true) && (return false) + (return true) && (return false) } fun returnInBinary2(): Boolean { - (return true) || (return false) + (return true) || (return false) } fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInCalls.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInCalls.fir.kt index 3cbdbe4eb0e..ee4f4927e82 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInCalls.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInCalls.fir.kt @@ -3,11 +3,11 @@ fun testArgumentInCall() { fun bar(i: Int, s: String, x: Any) {} - bar(1, todo(), "") + bar(1, todo(), "") } fun testArgumentInVariableAsFunctionCall(f: (Any) -> Unit) { - f(todo()) + f(todo()) } -fun todo(): Nothing = throw Exception() \ No newline at end of file +fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInDeadCode.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInDeadCode.fir.kt index f18f12bb79c..39c081c518c 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInDeadCode.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInDeadCode.fir.kt @@ -2,22 +2,22 @@ fun unreachable0() { return - return todo() + return todo() } fun unreachable2() { return - val a = todo() + val a = todo() } fun unreachable3() { return - bar(todo()) + bar(todo()) } fun unreachable4(array: Array) { return - array[todo()] + array[todo()] } fun bar(a: Any) {} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInIf.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInIf.fir.kt index bd22f23be28..749e31cdc70 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInIf.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInIf.fir.kt @@ -1,5 +1,5 @@ fun testIf() { - if (todo()) 1 else 2 + if (todo()) 1 else 2 } fun testIf1(b: Boolean) { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.fir.kt deleted file mode 100644 index 3048ba34877..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.fir.kt +++ /dev/null @@ -1,13 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -fun testCompound() { - operator fun Nothing.get(i: Int) {} - todo()!![12] -} - -fun testCompound1() { - operator fun Int.times(s: String): Array = throw Exception() - (todo() * "")[1] -} - -fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.kt index 644fcf114fd..346b657cc2f 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInInnerExpressions.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER fun testCompound() { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.fir.kt index 264569235d6..c7e460d1228 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLocalDeclarations.fir.kt @@ -1,13 +1,13 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER fun testObject() { - object : Foo(todo()) { + object : Foo(todo()) { fun foo() = 1 } } fun testObjectExpression() { - val a = object : Foo(todo()) { + val a = object : Foo(todo()) { fun foo() = 1 } } @@ -15,13 +15,13 @@ fun testObjectExpression() { fun testObjectExpression1() { fun bar(i: Int, x: Any) {} - bar(1, object : Foo(todo()) { + bar(1, object : Foo(todo()) { fun foo() = 1 }) } fun testClassDeclaration() { - class C : Foo(todo()) {} + class C : Foo(todo()) {} bar() } @@ -33,4 +33,4 @@ fun testFunctionDefaultArgument() { open class Foo(i: Int) {} fun todo(): Nothing = throw Exception() -fun bar() {} \ No newline at end of file +fun bar() {} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.fir.kt deleted file mode 100644 index 533b7798499..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.fir.kt +++ /dev/null @@ -1,21 +0,0 @@ -fun testFor() { - operator fun Nothing.iterator() = (0..1).iterator() - - for (i in todo()) {} -} - -fun testWhile() { - while (todo()) { - } -} - -fun testDoWhile() { - do { - - } while(todo()) - - bar() -} - -fun todo(): Nothing = throw Exception() -fun bar() {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.kt index ecb096a9ae5..aa4bcb75641 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInLoops.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL + fun testFor() { operator fun Nothing.iterator() = (0..1).iterator() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.fir.kt deleted file mode 100644 index 704b1f3e7b8..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.fir.kt +++ /dev/null @@ -1,5 +0,0 @@ -fun testReturn() { - return todo() -} - -fun todo(): Nothing = throw Exception() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.kt index b932880fcc6..98b865dadd0 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInReturn.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL fun testReturn() { return todo() } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInUnaryExpr.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInUnaryExpr.fir.kt index 6b80750ba54..6b3db517c27 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInUnaryExpr.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInUnaryExpr.fir.kt @@ -1,15 +1,15 @@ fun testPrefix() { operator fun Any.not() {} - !todo() + !todo() } fun testPostfixWithCall(n: Nothing) { operator fun Nothing.inc(): Nothing = this - n++ + n++ } fun testPostfixSpecial() { - todo()!! + todo()!! } fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInWhileFromBreak.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInWhileFromBreak.fir.kt index ae4eb52ba73..31aacf7e1e7 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInWhileFromBreak.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInWhileFromBreak.fir.kt @@ -3,36 +3,36 @@ fun bar(a: Any, b: Any) {} fun test(arr: Array) { while (true) { - foo(break) + foo(break) } while (true) { - bar(arr, break) + bar(arr, break) } while (true) { - arr[break] + arr[break] } while (true) { - arr[1] = break + arr[1] = break } while (true) { break - foo(1) + foo(1) } while (true) { - var x = 1 + var x = 1 break - x = 2 + x = 2 } while (true) { - var x = 1 - x = break + var x = 1 + x = break } // TODO: bug, should be fixed in CFA @@ -50,6 +50,6 @@ fun test(arr: Array) { } while (true) { - break ?: null + break ?: null } } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.fir.kt deleted file mode 100644 index 514476f04fb..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -//KT-3162 More precise try-finally error marking - -fun foo(x: String) : String { - val a = try { - x - } finally { - try { - } catch (e: Exception) { - } - return x - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.kt index a459cf6cf91..ce67a32b3c3 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt3162tryAsInitializer.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL //KT-3162 More precise try-finally error marking fun foo(x: String) : String { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.fir.kt deleted file mode 100644 index 9401436a8c7..00000000000 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.fir.kt +++ /dev/null @@ -1,25 +0,0 @@ -//KT-5200 Mark unreachable code in lambdas - -fun test1(): String { - doCall local@ { - throw NullPointerException() - "b3" //unmarked - } - - return "OK" -} - -fun test2(nonLocal: String, b: Boolean): String { - doCall local@ { - if (b) { - return@local "b1" - } else { - return@local "b2" - } - "b3" //unmarked - } - - return nonLocal -} - -inline fun doCall(block: ()-> String) = block() diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt index 3bacd0f0967..0dd62370324 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL //KT-5200 Mark unreachable code in lambdas fun test1(): String { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.fir.kt index 723ab38f1fd..787f7bbacc7 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.fir.kt @@ -2,10 +2,10 @@ inline fun myRun(b: () -> Unit) = b() fun foo() { - var a: Int + var a: Int return - myRun { + myRun { return - } + } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/severalJumpOutsFromInlinedLambda.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/severalJumpOutsFromInlinedLambda.fir.kt index 8bc4b931535..021c6028789 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/severalJumpOutsFromInlinedLambda.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/controlflow/flowInlining/severalJumpOutsFromInlinedLambda.fir.kt @@ -76,4 +76,4 @@ fun nonLocalReturnAndOrdinaryExit(b: Boolean) { } x.inc() s.length -} \ No newline at end of file +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt index 52d4c220594..9dfefab2543 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticCodeMetaInfo.kt @@ -19,20 +19,17 @@ object FirMetaInfoUtils { class FirDiagnosticCodeMetaInfo( val diagnostic: FirDiagnostic, - renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration + renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration, + private val range: TextRange ) : CodeMetaInfo { - private val textRangeFromClassicDiagnostic: TextRange = run { - diagnostic.factory.defaultPositioningStrategy.markDiagnostic(diagnostic).first() - } - override var renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration = renderConfiguration private set override val start: Int - get() = textRangeFromClassicDiagnostic.startOffset + get() = range.startOffset override val end: Int - get() = textRangeFromClassicDiagnostic.endOffset + get() = range.endOffset override val tag: String get() = renderConfiguration.getTag(this) diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt index 350a62e2c50..85085e31d91 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt @@ -81,12 +81,17 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes if (LanguageSettingsDirectives.API_VERSION in module.directives) { diagnostics = diagnostics.filter { it.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name } } - val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic -> - if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name, diagnostic.severity)) return@mapNotNull null + val diagnosticsMetadataInfos = diagnostics.flatMap { diagnostic -> + if (!diagnosticsService.shouldRenderDiagnostic( + module, + diagnostic.factory.name, + diagnostic.severity + ) + ) return@flatMap emptyList() // SYNTAX errors will be reported later - if (diagnostic.factory == FirErrors.SYNTAX) return@mapNotNull null - if (!diagnostic.isValid) return@mapNotNull null - diagnostic.toMetaInfo(file, lightTreeEnabled, lightTreeComparingModeEnabled) + if (diagnostic.factory == FirErrors.SYNTAX) return@flatMap emptyList() + if (!diagnostic.isValid) return@flatMap emptyList() + diagnostic.toMetaInfos(file, lightTreeEnabled, lightTreeComparingModeEnabled) } globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos) collectSyntaxDiagnostics(file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) @@ -94,22 +99,25 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes } } - private fun FirDiagnostic.toMetaInfo( + private fun FirDiagnostic.toMetaInfos( file: TestFile, lightTreeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean, forceRenderArguments: Boolean = false - ): FirDiagnosticCodeMetaInfo { - val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs) - val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo) - .any { it.description != null } - if (shouldRenderArguments) { - metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs) + ): List { + val ranges = factory.defaultPositioningStrategy.markDiagnostic(this) + return ranges.map { range -> + val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs, range) + val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo) + .any { it.description != null } + if (shouldRenderArguments) { + metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs) + } + if (lightTreeComparingModeEnabled) { + metaInfo.attributes += if (lightTreeEnabled) PsiLightTreeMetaInfoProcessor.LT else PsiLightTreeMetaInfoProcessor.PSI + } + metaInfo } - if (lightTreeComparingModeEnabled) { - metaInfo.attributes += if (lightTreeEnabled) PsiLightTreeMetaInfoProcessor.LT else PsiLightTreeMetaInfoProcessor.PSI - } - return metaInfo } @OptIn(InternalDiagnosticFactoryMethod::class) @@ -120,14 +128,14 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes lightTreeComparingModeEnabled: Boolean ) { val metaInfos = if (firFile.psi != null) { - AnalyzingUtils.getSyntaxErrorRanges(firFile.psi!!).map { + AnalyzingUtils.getSyntaxErrorRanges(firFile.psi!!).flatMap { FirErrors.SYNTAX.on(FirRealPsiSourceElement(it), positioningStrategy = null) - .toMetaInfo(testFile, lightTreeEnabled, lightTreeComparingModeEnabled) + .toMetaInfos(testFile, lightTreeEnabled, lightTreeComparingModeEnabled) } } else { - collectLightTreeSyntaxErrors(firFile).map { sourceElement -> + collectLightTreeSyntaxErrors(firFile).flatMap { sourceElement -> FirErrors.SYNTAX.on(sourceElement, positioningStrategy = null) - .toMetaInfo(testFile, lightTreeEnabled, lightTreeComparingModeEnabled) + .toMetaInfos(testFile, lightTreeEnabled, lightTreeComparingModeEnabled) } } @@ -183,7 +191,7 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes }.let(firFile::accept) globalMetadataInfoHandler.addMetadataInfosForFile( testFile, - result.map { it.toMetaInfo(testFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments = true) } + result.flatMap { it.toMetaInfos(testFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments = true) } ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt index e7b09ec0978..177ed7d9e47 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/AbstractFirDiagnosticTest.kt @@ -107,7 +107,10 @@ fun TestConfigurationBuilder.baseFirDiagnosticTestConfiguration( } } - forTestsMatching("compiler/fir/analysis-tests/testData/resolve/extendedCheckers/*") { + forTestsMatching( + "compiler/fir/analysis-tests/testData/resolve/extendedCheckers/*" or + "compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/*" + ) { defaultDirectives { +WITH_EXTENDED_CHECKERS } diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt index 03e84f05aa2..9e1a76332a9 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.expressions.FirExpression @@ -306,6 +307,14 @@ private object FirToKtConversionCreator { "firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)", KtFunctionLikeSymbol::class.createType(), importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction") + ), + FirSourceElement::class to HLFunctionCallConversion( + "({0} as FirPsiSourceElement).psi", + PsiElement::class.createType(), + importsToAdd = listOf( + "org.jetbrains.kotlin.fir.psi", + "org.jetbrains.kotlin.fir.FirPsiSourceElement" + ) ) ) diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 365baed87a1..d61da560836 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics import com.intellij.psi.PsiElement import com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.fir.FirPsiSourceElement import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors @@ -2595,6 +2596,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.UNREACHABLE_CODE) { firDiagnostic -> + UnreachableCodeImpl( + firDiagnostic.a.map { firSourceElement -> + (firSourceElement as FirPsiSourceElement).psi + }, + firDiagnostic.b.map { firSourceElement -> + (firSourceElement as FirPsiSourceElement).psi + }, + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.UNSAFE_CALL) { firDiagnostic -> UnsafeCallImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 865cad868c4..5f98f716a52 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1819,6 +1819,12 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val property: KtSymbol } + abstract class UnreachableCode : KtFirDiagnostic() { + override val diagnosticClass get() = UnreachableCode::class + abstract val reachable: List + abstract val unreachable: List + } + abstract class UnsafeCall : KtFirDiagnostic() { override val diagnosticClass get() = UnsafeCall::class abstract val receiverType: KtType diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index f6b506da8d0..eacdba98564 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -2930,6 +2930,15 @@ internal class InitializationBeforeDeclarationImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class UnreachableCodeImpl( + override val reachable: List, + override val unreachable: List, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.UnreachableCode(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class UnsafeCallImpl( override val receiverType: KtType, override val receiverExpression: KtExpression?,