[FIR] UNREACHABLE_CODE diagnostic (wip)

Implementation for PSI only
This commit is contained in:
Andrey Zinovyev
2021-07-29 12:39:47 +03:00
committed by TeamCityServer
parent dcd61c292d
commit ec4cbfef59
47 changed files with 348 additions and 220 deletions
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.* import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.*
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
@@ -943,6 +944,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val INITIALIZATION_BEFORE_DECLARATION by error<KtExpression>() { val INITIALIZATION_BEFORE_DECLARATION by error<KtExpression>() {
parameter<Symbol>("property") parameter<Symbol>("property")
} }
val UNREACHABLE_CODE by warning<KtElement>(PositioningStrategy.UNREACHABLE_CODE) {
parameter<Set<FirSourceElement>>("reachable")
parameter<Set<FirSourceElement>>("unreachable")
}
} }
val NULLABILITY by object : DiagnosticGroup("Nullability") { val NULLABILITY by object : DiagnosticGroup("Nullability") {
@@ -97,6 +97,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
DECLARATION_WITH_BODY, DECLARATION_WITH_BODY,
INCOMPATIBLE_DECLARATION, INCOMPATIBLE_DECLARATION,
ACTUAL_DECLARATION_NAME, ACTUAL_DECLARATION_NAME,
UNREACHABLE_CODE,
; ;
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.descriptors.EffectiveVisibility
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData 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.analysis.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -505,6 +506,7 @@ object FirErrors {
val WRONG_IMPLIES_CONDITION by warning0<PsiElement>() val WRONG_IMPLIES_CONDITION by warning0<PsiElement>()
val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0<KtVariableDeclaration>(SourceElementPositioningStrategies.DECLARATION_NAME) val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0<KtVariableDeclaration>(SourceElementPositioningStrategies.DECLARATION_NAME)
val INITIALIZATION_BEFORE_DECLARATION by error1<KtExpression, FirBasedSymbol<*>>() val INITIALIZATION_BEFORE_DECLARATION by error1<KtExpression, FirBasedSymbol<*>>()
val UNREACHABLE_CODE by warning2<KtElement, Set<FirSourceElement>, Set<FirSourceElement>>(SourceElementPositioningStrategies.UNREACHABLE_CODE)
// Nullability // Nullability
val UNSAFE_CALL by error2<PsiElement, ConeKotlinType, FirExpression?>(SourceElementPositioningStrategies.DOT_BY_QUALIFIED) val UNSAFE_CALL by error2<PsiElement, ConeKotlinType, FirExpression?>(SourceElementPositioningStrategies.DOT_BY_QUALIFIED)
@@ -30,6 +30,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker> override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
get() = setOf( get() = setOf(
UnusedChecker, UnusedChecker,
UnreachableCodeChecker,
) )
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker> override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
@@ -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<FirElement>()
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<CFGNode<*>> = mutableListOf()): List<CFGNode<*>> {
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<FirElement>) {
acceptChildren(CollectNodesVisitor(nodes))
}
private class CollectNodesVisitor(private val nodes: MutableSet<FirElement>) : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
nodes.add(element)
element.acceptChildren(this)
}
}
}
@@ -67,6 +67,7 @@ object UnusedChecker : FirControlFlowChecker() {
override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
val variableSymbol = node.fir.symbol val variableSymbol = node.fir.symbol
if (node.fir.source == null) return
if (variableSymbol.isLoopIterator) return if (variableSymbol.isLoopIterator) return
val dataPerNode = data[node] ?: return val dataPerNode = data[node] ?: return
for (dataPerLabel in dataPerNode.values) { for (dataPerLabel in dataPerNode.values) {
@@ -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_LATEINIT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNNECESSARY_NOT_NULL_ASSERTION 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.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_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNRESOLVED_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_CALL 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(LEAKED_IN_PLACE_LAMBDA, "Leaked in-place lambda: {2}", SYMBOL)
map.put(FirErrors.WRONG_IMPLIES_CONDITION, "Wrong implies condition") map.put(FirErrors.WRONG_IMPLIES_CONDITION, "Wrong implies condition")
map.put(UNREACHABLE_CODE, "Unreachable code", NOT_RENDERED, NOT_RENDERED)
// Nullability // Nullability
map.put( map.put(
@@ -23,6 +23,10 @@ sealed class AbstractFirDiagnosticFactory(
override fun toString(): String { override fun toString(): String {
return name return name
} }
protected fun checkMyDiagnostic(diagnostic: FirDiagnostic) {
require(diagnostic.factory === this) { "$diagnostic should be of this factory" }
}
} }
class FirDiagnosticFactory0( class FirDiagnosticFactory0(
@@ -79,6 +83,11 @@ class FirDiagnosticFactory1<A>(
else -> incorrectElement(element) else -> incorrectElement(element)
} }
} }
fun extractArgument(diagnostic: FirDiagnostic): A {
checkMyDiagnostic(diagnostic)
return (diagnostic as FirDiagnosticWithParameters1<A>).a
}
} }
class FirDiagnosticFactory2<A, B>( class FirDiagnosticFactory2<A, B>(
@@ -115,6 +124,12 @@ class FirDiagnosticFactory2<A, B>(
else -> incorrectElement(element) else -> incorrectElement(element)
} }
} }
fun extractArguments(diagnostic: FirDiagnostic): Pair<A, B> {
checkMyDiagnostic(diagnostic)
val typed = diagnostic as FirDiagnosticWithParameters2<A, B>
return Pair(typed.a, typed.b)
}
} }
class FirDiagnosticFactory3<A, B, C>( class FirDiagnosticFactory3<A, B, C>(
@@ -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<in E : PsiElement> : PositioningStrategy<E>(),
FirDiagnosticPositioningStrategy<FirPsiSourceElement>
object FirPsiPositioningStrategies {
val UNREACHABLE_CODE = object : FirPsiPositioningStrategy<PsiElement>() {
override fun markFirDiagnostic(element: FirPsiSourceElement, diagnostic: FirDiagnostic): List<TextRange> {
//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<FirSourceElement>, Set<FirSourceElement>>
return UnreachableCode.getUnreachableTextRanges(
element.psi as KtElement,
typed.a.mapNotNull { it.psi as? KtElement }.toSet(),
typed.b.mapNotNull { it.psi as? KtElement }.toSet()
)
}
}
}
@@ -10,12 +10,22 @@ import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.psi.TokenType import com.intellij.psi.TokenType
import com.intellij.util.diff.FlyweightCapableTreeStructure import com.intellij.util.diff.FlyweightCapableTreeStructure
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.fir.analysis.checkers.getChildren
import org.jetbrains.kotlin.lexer.KtSingleValueToken import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE
open class LightTreePositioningStrategy { interface FirDiagnosticPositioningStrategy<E : FirSourceElement> {
fun markFirDiagnostic(element: E, diagnostic: FirDiagnostic): List<TextRange>
}
open class LightTreePositioningStrategy : FirDiagnosticPositioningStrategy<FirSourceElement> {
override fun markFirDiagnostic(element: FirSourceElement, diagnostic: FirDiagnostic): List<TextRange> {
return mark(element.lighterASTNode, element.startOffset, element.endOffset, element.treeStructure)
}
open fun mark( open fun mark(
node: LighterASTNode, node: LighterASTNode,
startOffset: Int, startOffset: Int,
@@ -273,6 +273,11 @@ object SourceElementPositioningStrategies {
PositioningStrategies.DECLARATION_WITH_BODY PositioningStrategies.DECLARATION_WITH_BODY
) )
val UNREACHABLE_CODE = SourceElementPositioningStrategy(
LightTreePositioningStrategies.DEFAULT, //todo
FirPsiPositioningStrategies.UNREACHABLE_CODE
)
// TODO // TODO
val ACTUAL_DECLARATION_NAME = DEFAULT val ACTUAL_DECLARATION_NAME = DEFAULT
val INCOMPATIBLE_DECLARATION = DEFAULT val INCOMPATIBLE_DECLARATION = DEFAULT
@@ -18,10 +18,14 @@ class SourceElementPositioningStrategy(
fun markDiagnostic(diagnostic: FirDiagnostic): List<TextRange> { fun markDiagnostic(diagnostic: FirDiagnostic): List<TextRange> {
val element = diagnostic.element val element = diagnostic.element
if (element is FirPsiSourceElement) { if (element is FirPsiSourceElement) {
@Suppress("UNCHECKED_CAST") return if (psiStrategy is FirPsiPositioningStrategy<*>) {
return psiStrategy.hackyMark(element.psi) 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 { fun isValid(element: FirSourceElement): Boolean {
@@ -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()
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
fun testCommasAndWhitespaces() { fun testCommasAndWhitespaces() {
@@ -1,18 +1,18 @@
package a package a
fun test1() { fun test1() {
bar( <!UNREACHABLE_CODE!>bar(<!>
11, 11,
todo(),//comment1 todo(),//comment1
""//comment2 <!UNREACHABLE_CODE!>""//comment2
) )<!>
} }
fun test2() { fun test2() {
bar(11, todo()/*comment1*/, ""/*comment2*/) <!UNREACHABLE_CODE!>bar(<!>11, todo()/*comment1*/, <!UNREACHABLE_CODE!>""/*comment2*/)<!>
} }
fun test3() { fun test3() {
bar(11, l@(todo()/*comment*/), "") <!UNREACHABLE_CODE!>bar(<!>11, <!UNREACHABLE_CODE!>l@(<!>todo()/*comment*/<!UNREACHABLE_CODE!>), "")<!>
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -1,11 +1,11 @@
fun testInvoke() { fun testInvoke() {
operator fun Nothing.invoke(): Nothing = this operator fun Nothing.invoke(): Nothing = this
todo()() todo()<!UNREACHABLE_CODE!>()<!>
} }
fun testInvokeWithLambda() { fun testInvokeWithLambda() {
operator fun Nothing.invoke(i: Int, f: () -> Int) = f operator fun Nothing.invoke(i: Int, f: () -> Int) = f
todo()(1){ 42 } todo()<!UNREACHABLE_CODE!>(1){ 42 }<!>
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -1,11 +1,11 @@
fun test11() { fun test11() {
fun Any.bar(i: Int) {} fun Any.bar(i: Int) {}
todo().bar(1) todo()<!UNREACHABLE_CODE!>.bar(1)<!>
} }
fun test12() { fun test12() {
fun Any.bar(i: Int) {} fun Any.bar(i: Int) {}
todo()<!UNNECESSARY_SAFE_CALL!>?.<!>bar(1) todo()<!UNREACHABLE_CODE!><!UNNECESSARY_SAFE_CALL!>?.<!>bar(1)<!>
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -3,80 +3,80 @@
fun t1() : Int{ fun t1() : Int{
return 0 return 0
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t1a() : Int { fun t1a() : Int {
<!RETURN_TYPE_MISMATCH!>return<!> <!RETURN_TYPE_MISMATCH!>return<!>
return 1 <!UNREACHABLE_CODE!>return 1<!>
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t1b() : Int { fun t1b() : Int {
return 1 return 1
return 1 <!UNREACHABLE_CODE!>return 1<!>
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t1c() : Int { fun t1c() : Int {
return 1 return 1
<!RETURN_TYPE_MISMATCH!>return<!> <!RETURN_TYPE_MISMATCH, UNREACHABLE_CODE!>return<!>
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t2() : Int { fun t2() : Int {
if (1 > 2) if (1 > 2)
return 1 return 1
else return 1 else return 1
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t2a() : Int { fun t2a() : Int {
if (1 > 2) { if (1 > 2) {
return 1 return 1
1 <!UNREACHABLE_CODE!>1<!>
} else { return 1 } else { return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t3() : Any { fun t3() : Any {
if (1 > 2) if (1 > 2)
return 2 return 2
else return "" else return ""
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t4(a : Boolean) : Int { fun t4(a : Boolean) : Int {
do { do {
return 1 return 1
} }
while (a) while (<!UNREACHABLE_CODE!>a<!>)
1 <!UNREACHABLE_CODE!>1<!>
} }
fun t4break(a : Boolean) : Int { fun t4break(a : Boolean) : Int {
do { do {
break break
} }
while (a) while (<!UNREACHABLE_CODE!>a<!>)
return 1 return 1
} }
fun t5() : Int { fun t5() : Int {
do { do {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
while (1 > 2) while (<!UNREACHABLE_CODE!>1 > 2<!>)
return 1 <!UNREACHABLE_CODE!>return 1<!>
} }
fun t6() : Int { fun t6() : Int {
while (1 > 2) { while (1 > 2) {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
return 1 return 1
} }
@@ -84,7 +84,7 @@ fun t6() : Int {
fun t6break() : Int { fun t6break() : Int {
while (1 > 2) { while (1 > 2) {
break break
2 <!UNREACHABLE_CODE!>2<!>
} }
return 1 return 1
} }
@@ -92,7 +92,7 @@ fun t6break() : Int {
fun t7(b : Int) : Int { fun t7(b : Int) : Int {
for (i in 1..b) { for (i in 1..b) {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
return 1 return 1
} }
@@ -100,7 +100,7 @@ fun t7(b : Int) : Int {
fun t7break(b : Int) : Int { fun t7break(b : Int) : Int {
for (i in 1..b) { for (i in 1..b) {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
return 1 return 1
} }
@@ -108,54 +108,54 @@ fun t7break(b : Int) : Int {
fun t7() : Int { fun t7() : Int {
try { try {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) { catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) {
2 2
} }
return 1 // this is OK, like in Java <!UNREACHABLE_CODE!>return 1<!> // this is OK, like in Java
} }
fun t8() : Int { fun t8() : Int {
try { try {
return 1 return 1
2 <!UNREACHABLE_CODE!>2<!>
} }
catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) { catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) {
return 1 return 1
2 2
} }
return 1 <!UNREACHABLE_CODE!>return 1<!>
} }
fun blockAndAndMismatch() : Boolean { fun blockAndAndMismatch() : Boolean {
(return true) || (return false) (return true) || (<!UNREACHABLE_CODE!>return false<!>)
return true <!UNREACHABLE_CODE!>return true<!>
} }
fun tf() : Int { fun tf() : Int {
try {return 1} finally{return 1} try {return 1} finally{return 1}
return 1 <!UNREACHABLE_CODE!>return 1<!>
} }
fun failtest(a : Int) : Int { fun failtest(a : Int) : Int {
if (fail() || true) { if (fail() || <!UNREACHABLE_CODE!>true<!>) {
} }
return 1 <!UNREACHABLE_CODE!>return 1<!>
} }
fun foo(a : Nothing) : Unit { fun foo(a : Nothing) : <!REDUNDANT_RETURN_UNIT_TYPE!>Unit<!> {
1 1
a a
2 <!UNREACHABLE_CODE!>2<!>
} }
fun fail() : Nothing { fun fail() : Nothing {
throw java.lang.RuntimeException() throw java.lang.RuntimeException()
} }
fun nullIsNotNothing() : Unit { fun nullIsNotNothing() : <!REDUNDANT_RETURN_UNIT_TYPE!>Unit<!> {
val x : Int? = 1 val x : Int? = 1
if (x != null) { if (x != null) {
return return
@@ -165,5 +165,5 @@ fun nullIsNotNothing() : Unit {
fun returnInWhile(a: Int) { fun returnInWhile(a: Int) {
do {return} do {return}
while (1 > a) while (<!UNREACHABLE_CODE!>1 > a<!>)
} }
@@ -1,17 +1,17 @@
package c package c
fun test1() { fun test1() {
val r: Nothing = null!! <!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>r<!>: Nothing =<!> null!!
} }
fun test2(a: A) { fun test2(a: A) {
a + a a + a
bar() <!UNREACHABLE_CODE!>bar()<!>
} }
fun test3() { fun test3() {
null!! null!!
bar() <!UNREACHABLE_CODE!>bar()<!>
} }
fun throwNPE(): Nothing = null!! fun throwNPE(): Nothing = null!!
@@ -1,40 +1,40 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
fun testArrayAccess1(array: Array<Any>) { fun testArrayAccess1(array: Array<Any>) {
array[todo()] array<!UNREACHABLE_CODE!>[<!>todo()<!UNREACHABLE_CODE!>]<!>
} }
fun testArrayAccess2() { fun testArrayAccess2() {
operator fun Nothing.get(i: Int, s: String) {} operator fun Nothing.get(i: Int, s: String) {}
todo()[1, ""] todo()<!UNREACHABLE_CODE!>[1, ""]<!>
} }
fun testAraryAccess3() { fun testAraryAccess3() {
operator fun Nothing.get(n: Nothing) {} operator fun Nothing.get(n: Nothing) {}
todo()[todo()] todo()<!UNREACHABLE_CODE!>[todo()]<!>
} }
fun testArrayAssignment1(array: Array<Any>) { fun testArrayAssignment1(array: Array<Any>) {
array[todo()] = 11 array<!UNREACHABLE_CODE!>[<!>todo()<!UNREACHABLE_CODE!>] = 11<!>
} }
fun testArrayAssignment2(array: Array<Any>) { fun testArrayAssignment2(array: Array<Any>) {
array[1] = todo() array<!UNREACHABLE_CODE!>[<!>1<!UNREACHABLE_CODE!>] =<!> todo()
} }
fun testArrayAssignment3(n: Nothing) { fun testArrayAssignment3(n: Nothing) {
operator fun Nothing.set(i: Int, j: Int) {} operator fun Nothing.set(i: Int, j: Int) {}
n[1] = 2 n<!UNREACHABLE_CODE!>[1] = 2<!>
} }
fun testArrayAssignment4(n: Nothing) { fun testArrayAssignment4(n: Nothing) {
operator fun Nothing.set(i: Int, a: Any) {} operator fun Nothing.set(i: Int, a: Any) {}
n[1] = todo() n<!UNREACHABLE_CODE!>[1] = todo()<!>
} }
fun testArrayPlusAssign(array: Array<Any>) { fun testArrayPlusAssign(array: Array<Any>) {
operator fun Any.plusAssign(a: Any) {} operator fun Any.plusAssign(a: Any) {}
array[1] += todo() array<!UNREACHABLE_CODE!>[<!>1<!UNREACHABLE_CODE!>] +=<!> todo()
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -1,18 +1,18 @@
fun testAssignment() { fun testAssignment() {
var a = 1 var <!UNUSED_VARIABLE!>a<!> = 1
a = todo() <!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>a<!> =<!> todo()
} }
fun testVariableDeclaration() { fun testVariableDeclaration() {
val a = todo() <!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>a<!> =<!> todo()
} }
fun testPlusAssign() { fun testPlusAssign() {
operator fun Int.plusAssign(i: Int) {} operator fun Int.plusAssign(i: Int) {}
var a = 1 <!CAN_BE_VAL!>var<!> a = 1
a += todo() a <!UNREACHABLE_CODE!>+=<!> todo()
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -1,14 +1,14 @@
fun testBinary1() { fun testBinary1() {
operator fun Int.times(s: String) {} operator fun Int.times(s: String) {}
todo() * "" todo() <!UNREACHABLE_CODE!>* ""<!>
} }
fun testBinary2() { fun testBinary2() {
"1" + todo() "1" <!UNREACHABLE_CODE!>+<!> todo()
} }
fun testElvis1() { fun testElvis1() {
todo() <!USELESS_ELVIS!>?: ""<!> todo() <!UNREACHABLE_CODE, USELESS_ELVIS!>?: ""<!>
} }
fun testElvis2(s: String?) { fun testElvis2(s: String?) {
@@ -24,15 +24,15 @@ fun testAnd1(b: Boolean) {
} }
fun testAnd2(b: Boolean) { fun testAnd2(b: Boolean) {
todo() && b todo() && <!UNREACHABLE_CODE!>b<!>
} }
fun returnInBinary1(): Boolean { fun returnInBinary1(): Boolean {
(return true) && (return false) (return true) && (<!UNREACHABLE_CODE!>return false<!>)
} }
fun returnInBinary2(): Boolean { fun returnInBinary2(): Boolean {
(return true) || (return false) (return true) || (<!UNREACHABLE_CODE!>return false<!>)
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -3,11 +3,11 @@
fun testArgumentInCall() { fun testArgumentInCall() {
fun bar(i: Int, s: String, x: Any) {} fun bar(i: Int, s: String, x: Any) {}
bar(1, todo(), "") <!UNREACHABLE_CODE!>bar(<!>1, todo(), <!UNREACHABLE_CODE!>"")<!>
} }
fun testArgumentInVariableAsFunctionCall(f: (Any) -> Unit) { fun testArgumentInVariableAsFunctionCall(f: (Any) -> Unit) {
f(todo()) <!UNREACHABLE_CODE!>f(<!>todo()<!UNREACHABLE_CODE!>)<!>
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -2,22 +2,22 @@
fun unreachable0() { fun unreachable0() {
return return
return todo() <!UNREACHABLE_CODE!>return todo()<!>
} }
fun unreachable2() { fun unreachable2() {
return return
val a = todo() <!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>a<!> = todo()<!>
} }
fun unreachable3() { fun unreachable3() {
return return
bar(todo()) <!UNREACHABLE_CODE!>bar(todo())<!>
} }
fun unreachable4(array: Array<Any>) { fun unreachable4(array: Array<Any>) {
return return
array[todo()] <!UNREACHABLE_CODE!>array[todo()]<!>
} }
fun bar(a: Any) {} fun bar(a: Any) {}
@@ -1,5 +1,5 @@
fun testIf() { fun testIf() {
if (todo()) 1 else 2 if (todo()) <!UNREACHABLE_CODE!>1<!> else <!UNREACHABLE_CODE!>2<!>
} }
fun testIf1(b: Boolean) { fun testIf1(b: Boolean) {
@@ -1,13 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun testCompound() {
operator fun Nothing.get(i: Int) {}
todo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>[12]
}
fun testCompound1() {
operator fun Int.times(s: String): Array<String> = throw Exception()
(todo() * "")[1]
}
fun todo(): Nothing = throw Exception()
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
fun testCompound() { fun testCompound() {
@@ -1,13 +1,13 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
fun testObject() { fun testObject() {
object : Foo(todo()) { object : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
fun foo() = 1 fun foo() = 1
} }
} }
fun testObjectExpression() { fun testObjectExpression() {
val a = object : Foo(todo()) { val <!UNUSED_VARIABLE!>a<!> = object : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
fun foo() = 1 fun foo() = 1
} }
} }
@@ -15,13 +15,13 @@ fun testObjectExpression() {
fun testObjectExpression1() { fun testObjectExpression1() {
fun bar(i: Int, x: Any) {} fun bar(i: Int, x: Any) {}
bar(1, object : Foo(todo()) { bar(1, object : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
fun foo() = 1 fun foo() = 1
}) })
} }
fun testClassDeclaration() { fun testClassDeclaration() {
class C : Foo(todo()) {} class C : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {}
bar() bar()
} }
@@ -33,4 +33,4 @@ fun testFunctionDefaultArgument() {
open class Foo(i: Int) {} open class Foo(i: Int) {}
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
fun bar() {} fun bar() {}
@@ -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() {}
@@ -1,3 +1,5 @@
// FIR_IDENTICAL
fun testFor() { fun testFor() {
operator fun Nothing.iterator() = (0..1).iterator() operator fun Nothing.iterator() = (0..1).iterator()
@@ -1,5 +0,0 @@
fun testReturn() {
return todo()
}
fun todo(): Nothing = throw Exception()
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun testReturn() { fun testReturn() {
<!UNREACHABLE_CODE!>return<!> todo() <!UNREACHABLE_CODE!>return<!> todo()
} }
@@ -1,15 +1,15 @@
fun testPrefix() { fun testPrefix() {
operator fun Any.not() {} operator fun Any.not() {}
!todo() <!UNREACHABLE_CODE!>!<!>todo()
} }
fun testPostfixWithCall(n: Nothing) { fun testPostfixWithCall(n: Nothing) {
operator fun Nothing.inc(): Nothing = this operator fun Nothing.inc(): Nothing = this
<!VAL_REASSIGNMENT!>n<!>++ <!VAL_REASSIGNMENT!>n<!><!UNREACHABLE_CODE!>++<!>
} }
fun testPostfixSpecial() { fun testPostfixSpecial() {
todo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!> todo()<!UNNECESSARY_NOT_NULL_ASSERTION, UNREACHABLE_CODE!>!!<!>
} }
fun todo(): Nothing = throw Exception() fun todo(): Nothing = throw Exception()
@@ -3,36 +3,36 @@ fun bar(a: Any, b: Any) {}
fun test(arr: Array<Int>) { fun test(arr: Array<Int>) {
while (true) { while (true) {
foo(break) <!UNREACHABLE_CODE!>foo(<!>break<!UNREACHABLE_CODE!>)<!>
} }
while (true) { while (true) {
bar(arr, break) <!UNREACHABLE_CODE!>bar(<!>arr, break<!UNREACHABLE_CODE!>)<!>
} }
while (true) { while (true) {
arr[break] arr<!UNREACHABLE_CODE!>[<!>break<!UNREACHABLE_CODE!>]<!>
} }
while (true) { while (true) {
arr[1] = break arr<!UNREACHABLE_CODE!>[<!>1<!UNREACHABLE_CODE!>] =<!> break
} }
while (true) { while (true) {
break break
foo(1) <!UNREACHABLE_CODE!>foo(1)<!>
} }
while (true) { while (true) {
var x = 1 var <!UNUSED_VARIABLE!>x<!> = 1
break break
x = 2 <!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>x<!> = 2<!>
} }
while (true) { while (true) {
var x = 1 var <!UNUSED_VARIABLE!>x<!> = 1
x = break <!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>x<!> =<!> break
} }
// TODO: bug, should be fixed in CFA // TODO: bug, should be fixed in CFA
@@ -50,6 +50,6 @@ fun test(arr: Array<Int>) {
} }
while (true) { while (true) {
break <!USELESS_ELVIS!>?: null<!> break <!UNREACHABLE_CODE, USELESS_ELVIS!>?: 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
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//KT-3162 More precise try-finally error marking //KT-3162 More precise try-finally error marking
fun foo(x: String) : String { fun foo(x: String) : String {
@@ -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()
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//KT-5200 Mark unreachable code in lambdas //KT-5200 Mark unreachable code in lambdas
fun test1(): String { fun test1(): String {
@@ -2,10 +2,10 @@
inline fun myRun(b: () -> Unit) = b() inline fun myRun(b: () -> Unit) = b()
fun foo() { fun foo() {
var a: Int <!CAN_BE_VAL!>var<!> <!UNUSED_VARIABLE!>a<!>: Int
return return
myRun { <!UNREACHABLE_CODE!>myRun {
return return
} }<!>
} }
@@ -76,4 +76,4 @@ fun nonLocalReturnAndOrdinaryExit(b: Boolean) {
} }
x.inc() x.inc()
s.length s.length
} }
@@ -19,20 +19,17 @@ object FirMetaInfoUtils {
class FirDiagnosticCodeMetaInfo( class FirDiagnosticCodeMetaInfo(
val diagnostic: FirDiagnostic, val diagnostic: FirDiagnostic,
renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration,
private val range: TextRange
) : CodeMetaInfo { ) : CodeMetaInfo {
private val textRangeFromClassicDiagnostic: TextRange = run {
diagnostic.factory.defaultPositioningStrategy.markDiagnostic(diagnostic).first()
}
override var renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration = renderConfiguration override var renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration = renderConfiguration
private set private set
override val start: Int override val start: Int
get() = textRangeFromClassicDiagnostic.startOffset get() = range.startOffset
override val end: Int override val end: Int
get() = textRangeFromClassicDiagnostic.endOffset get() = range.endOffset
override val tag: String override val tag: String
get() = renderConfiguration.getTag(this) get() = renderConfiguration.getTag(this)
@@ -81,12 +81,17 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
if (LanguageSettingsDirectives.API_VERSION in module.directives) { if (LanguageSettingsDirectives.API_VERSION in module.directives) {
diagnostics = diagnostics.filter { it.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name } diagnostics = diagnostics.filter { it.factory.name != FirErrors.NEWER_VERSION_IN_SINCE_KOTLIN.name }
} }
val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic -> val diagnosticsMetadataInfos = diagnostics.flatMap { diagnostic ->
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name, diagnostic.severity)) return@mapNotNull null if (!diagnosticsService.shouldRenderDiagnostic(
module,
diagnostic.factory.name,
diagnostic.severity
)
) return@flatMap emptyList()
// SYNTAX errors will be reported later // SYNTAX errors will be reported later
if (diagnostic.factory == FirErrors.SYNTAX) return@mapNotNull null if (diagnostic.factory == FirErrors.SYNTAX) return@flatMap emptyList()
if (!diagnostic.isValid) return@mapNotNull null if (!diagnostic.isValid) return@flatMap emptyList()
diagnostic.toMetaInfo(file, lightTreeEnabled, lightTreeComparingModeEnabled) diagnostic.toMetaInfos(file, lightTreeEnabled, lightTreeComparingModeEnabled)
} }
globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos) globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos)
collectSyntaxDiagnostics(file, firFile, lightTreeEnabled, lightTreeComparingModeEnabled) 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, file: TestFile,
lightTreeEnabled: Boolean, lightTreeEnabled: Boolean,
lightTreeComparingModeEnabled: Boolean, lightTreeComparingModeEnabled: Boolean,
forceRenderArguments: Boolean = false forceRenderArguments: Boolean = false
): FirDiagnosticCodeMetaInfo { ): List<FirDiagnosticCodeMetaInfo> {
val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs) val ranges = factory.defaultPositioningStrategy.markDiagnostic(this)
val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo) return ranges.map { range ->
.any { it.description != null } val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs, range)
if (shouldRenderArguments) { val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs) .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) @OptIn(InternalDiagnosticFactoryMethod::class)
@@ -120,14 +128,14 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes
lightTreeComparingModeEnabled: Boolean lightTreeComparingModeEnabled: Boolean
) { ) {
val metaInfos = if (firFile.psi != null) { val metaInfos = if (firFile.psi != null) {
AnalyzingUtils.getSyntaxErrorRanges(firFile.psi!!).map { AnalyzingUtils.getSyntaxErrorRanges(firFile.psi!!).flatMap {
FirErrors.SYNTAX.on(FirRealPsiSourceElement(it), positioningStrategy = null) FirErrors.SYNTAX.on(FirRealPsiSourceElement(it), positioningStrategy = null)
.toMetaInfo(testFile, lightTreeEnabled, lightTreeComparingModeEnabled) .toMetaInfos(testFile, lightTreeEnabled, lightTreeComparingModeEnabled)
} }
} else { } else {
collectLightTreeSyntaxErrors(firFile).map { sourceElement -> collectLightTreeSyntaxErrors(firFile).flatMap { sourceElement ->
FirErrors.SYNTAX.on(sourceElement, positioningStrategy = null) 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) }.let(firFile::accept)
globalMetadataInfoHandler.addMetadataInfosForFile( globalMetadataInfoHandler.addMetadataInfosForFile(
testFile, testFile,
result.map { it.toMetaInfo(testFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments = true) } result.flatMap { it.toMetaInfos(testFile, lightTreeEnabled, lightTreeComparingModeEnabled, forceRenderArguments = true) }
) )
} }
@@ -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 { defaultDirectives {
+WITH_EXTENDED_CHECKERS +WITH_EXTENDED_CHECKERS
} }
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.fir.FirModuleData 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.checkers.generator.diagnostics.model.*
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -306,6 +307,14 @@ private object FirToKtConversionCreator {
"firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)", "firSymbolBuilder.functionLikeBuilder.buildFunctionSymbol({0}.fir)",
KtFunctionLikeSymbol::class.createType(), KtFunctionLikeSymbol::class.createType(),
importsToAdd = listOf("org.jetbrains.kotlin.fir.declarations.FirSimpleFunction") 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"
)
) )
) )
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement 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.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
@@ -2595,6 +2596,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, 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 -> add(FirErrors.UNSAFE_CALL) { firDiagnostic ->
UnsafeCallImpl( UnsafeCallImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
@@ -1819,6 +1819,12 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val property: KtSymbol abstract val property: KtSymbol
} }
abstract class UnreachableCode : KtFirDiagnostic<KtElement>() {
override val diagnosticClass get() = UnreachableCode::class
abstract val reachable: List<PsiElement>
abstract val unreachable: List<PsiElement>
}
abstract class UnsafeCall : KtFirDiagnostic<PsiElement>() { abstract class UnsafeCall : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = UnsafeCall::class override val diagnosticClass get() = UnsafeCall::class
abstract val receiverType: KtType abstract val receiverType: KtType
@@ -2930,6 +2930,15 @@ internal class InitializationBeforeDeclarationImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
} }
internal class UnreachableCodeImpl(
override val reachable: List<PsiElement>,
override val unreachable: List<PsiElement>,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.UnreachableCode(), KtAbstractFirDiagnostic<KtElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class UnsafeCallImpl( internal class UnsafeCallImpl(
override val receiverType: KtType, override val receiverType: KtType,
override val receiverExpression: KtExpression?, override val receiverExpression: KtExpression?,