[FIR] UNREACHABLE_CODE diagnostic (wip)
Implementation for PSI only
This commit is contained in:
committed by
TeamCityServer
parent
dcd61c292d
commit
ec4cbfef59
+6
@@ -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<KtExpression>() {
|
||||
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") {
|
||||
|
||||
+1
@@ -97,6 +97,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
|
||||
DECLARATION_WITH_BODY,
|
||||
INCOMPATIBLE_DECLARATION,
|
||||
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.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<PsiElement>()
|
||||
val VARIABLE_WITH_NO_TYPE_NO_INITIALIZER by error0<KtVariableDeclaration>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val INITIALIZATION_BEFORE_DECLARATION by error1<KtExpression, FirBasedSymbol<*>>()
|
||||
val UNREACHABLE_CODE by warning2<KtElement, Set<FirSourceElement>, Set<FirSourceElement>>(SourceElementPositioningStrategies.UNREACHABLE_CODE)
|
||||
|
||||
// Nullability
|
||||
val UNSAFE_CALL by error2<PsiElement, ConeKotlinType, FirExpression?>(SourceElementPositioningStrategies.DOT_BY_QUALIFIED)
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
|
||||
override val controlFlowAnalyserCheckers: Set<FirControlFlowChecker>
|
||||
get() = setOf(
|
||||
UnusedChecker,
|
||||
UnreachableCodeChecker,
|
||||
)
|
||||
|
||||
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
|
||||
|
||||
+81
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -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) {
|
||||
|
||||
+2
@@ -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(
|
||||
|
||||
+15
@@ -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<A>(
|
||||
else -> incorrectElement(element)
|
||||
}
|
||||
}
|
||||
|
||||
fun extractArgument(diagnostic: FirDiagnostic): A {
|
||||
checkMyDiagnostic(diagnostic)
|
||||
return (diagnostic as FirDiagnosticWithParameters1<A>).a
|
||||
}
|
||||
}
|
||||
|
||||
class FirDiagnosticFactory2<A, B>(
|
||||
@@ -115,6 +124,12 @@ class FirDiagnosticFactory2<A, B>(
|
||||
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>(
|
||||
|
||||
+36
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+11
-1
@@ -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<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(
|
||||
node: LighterASTNode,
|
||||
startOffset: Int,
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+7
-3
@@ -18,10 +18,14 @@ class SourceElementPositioningStrategy(
|
||||
fun markDiagnostic(diagnostic: FirDiagnostic): List<TextRange> {
|
||||
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 {
|
||||
|
||||
Vendored
-12
@@ -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
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun testCommasAndWhitespaces() {
|
||||
|
||||
+5
-5
@@ -1,18 +1,18 @@
|
||||
package a
|
||||
|
||||
fun test1() {
|
||||
bar(
|
||||
<!UNREACHABLE_CODE!>bar(<!>
|
||||
11,
|
||||
todo(),//comment1
|
||||
""//comment2
|
||||
)
|
||||
<!UNREACHABLE_CODE!>""//comment2
|
||||
)<!>
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
bar(11, todo()/*comment1*/, ""/*comment2*/)
|
||||
<!UNREACHABLE_CODE!>bar(<!>11, todo()/*comment1*/, <!UNREACHABLE_CODE!>""/*comment2*/)<!>
|
||||
}
|
||||
fun test3() {
|
||||
bar(11, l@(todo()/*comment*/), "")
|
||||
<!UNREACHABLE_CODE!>bar(<!>11, <!UNREACHABLE_CODE!>l@(<!>todo()/*comment*/<!UNREACHABLE_CODE!>), "")<!>
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
Vendored
+3
-3
@@ -1,11 +1,11 @@
|
||||
fun testInvoke() {
|
||||
operator fun Nothing.invoke(): Nothing = this
|
||||
todo()()
|
||||
todo()<!UNREACHABLE_CODE!>()<!>
|
||||
}
|
||||
|
||||
fun testInvokeWithLambda() {
|
||||
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()
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
fun test11() {
|
||||
fun Any.bar(i: Int) {}
|
||||
todo().bar(1)
|
||||
todo()<!UNREACHABLE_CODE!>.bar(1)<!>
|
||||
}
|
||||
|
||||
fun test12() {
|
||||
fun Any.bar(i: Int) {}
|
||||
todo()<!UNNECESSARY_SAFE_CALL!>?.<!>bar(1)
|
||||
todo()<!UNREACHABLE_CODE!><!UNNECESSARY_SAFE_CALL!>?.<!>bar(1)<!>
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
Vendored
+35
-35
@@ -3,80 +3,80 @@
|
||||
|
||||
fun t1() : Int{
|
||||
return 0
|
||||
1
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t1a() : Int {
|
||||
<!RETURN_TYPE_MISMATCH!>return<!>
|
||||
return 1
|
||||
1
|
||||
<!UNREACHABLE_CODE!>return 1<!>
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t1b() : Int {
|
||||
return 1
|
||||
return 1
|
||||
1
|
||||
<!UNREACHABLE_CODE!>return 1<!>
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t1c() : Int {
|
||||
return 1
|
||||
<!RETURN_TYPE_MISMATCH!>return<!>
|
||||
1
|
||||
<!RETURN_TYPE_MISMATCH, UNREACHABLE_CODE!>return<!>
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t2() : Int {
|
||||
if (1 > 2)
|
||||
return 1
|
||||
else return 1
|
||||
1
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t2a() : Int {
|
||||
if (1 > 2) {
|
||||
return 1
|
||||
1
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
} else { return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
1
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t3() : Any {
|
||||
if (1 > 2)
|
||||
return 2
|
||||
else return ""
|
||||
1
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t4(a : Boolean) : Int {
|
||||
do {
|
||||
return 1
|
||||
}
|
||||
while (a)
|
||||
1
|
||||
while (<!UNREACHABLE_CODE!>a<!>)
|
||||
<!UNREACHABLE_CODE!>1<!>
|
||||
}
|
||||
|
||||
fun t4break(a : Boolean) : Int {
|
||||
do {
|
||||
break
|
||||
}
|
||||
while (a)
|
||||
while (<!UNREACHABLE_CODE!>a<!>)
|
||||
return 1
|
||||
}
|
||||
|
||||
fun t5() : Int {
|
||||
do {
|
||||
return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
while (1 > 2)
|
||||
return 1
|
||||
while (<!UNREACHABLE_CODE!>1 > 2<!>)
|
||||
<!UNREACHABLE_CODE!>return 1<!>
|
||||
}
|
||||
|
||||
fun t6() : Int {
|
||||
while (1 > 2) {
|
||||
return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
return 1
|
||||
}
|
||||
@@ -84,7 +84,7 @@ fun t6() : Int {
|
||||
fun t6break() : Int {
|
||||
while (1 > 2) {
|
||||
break
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
return 1
|
||||
}
|
||||
@@ -92,7 +92,7 @@ fun t6break() : Int {
|
||||
fun t7(b : Int) : Int {
|
||||
for (i in 1..b) {
|
||||
return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>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
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
return 1
|
||||
}
|
||||
@@ -108,54 +108,54 @@ fun t7break(b : Int) : Int {
|
||||
fun t7() : Int {
|
||||
try {
|
||||
return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) {
|
||||
2
|
||||
}
|
||||
return 1 // this is OK, like in Java
|
||||
<!UNREACHABLE_CODE!>return 1<!> // this is OK, like in Java
|
||||
}
|
||||
|
||||
fun t8() : Int {
|
||||
try {
|
||||
return 1
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
catch (<!THROWABLE_TYPE_MISMATCH!>e : Any<!>) {
|
||||
return 1
|
||||
2
|
||||
}
|
||||
return 1
|
||||
<!UNREACHABLE_CODE!>return 1<!>
|
||||
}
|
||||
|
||||
fun blockAndAndMismatch() : Boolean {
|
||||
(return true) || (return false)
|
||||
return true
|
||||
(return true) || (<!UNREACHABLE_CODE!>return false<!>)
|
||||
<!UNREACHABLE_CODE!>return true<!>
|
||||
}
|
||||
|
||||
fun tf() : Int {
|
||||
try {return 1} finally{return 1}
|
||||
return 1
|
||||
<!UNREACHABLE_CODE!>return 1<!>
|
||||
}
|
||||
|
||||
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
|
||||
a
|
||||
2
|
||||
<!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
|
||||
fun fail() : Nothing {
|
||||
throw java.lang.RuntimeException()
|
||||
}
|
||||
|
||||
fun nullIsNotNothing() : Unit {
|
||||
fun nullIsNotNothing() : <!REDUNDANT_RETURN_UNIT_TYPE!>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 (<!UNREACHABLE_CODE!>1 > a<!>)
|
||||
}
|
||||
|
||||
compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeFromDifferentSources.fir.kt
Vendored
+3
-3
@@ -1,17 +1,17 @@
|
||||
package c
|
||||
|
||||
fun test1() {
|
||||
val r: Nothing = null!!
|
||||
<!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>r<!>: Nothing =<!> null!!
|
||||
}
|
||||
|
||||
fun test2(a: A) {
|
||||
a + a
|
||||
bar()
|
||||
<!UNREACHABLE_CODE!>bar()<!>
|
||||
}
|
||||
|
||||
fun test3() {
|
||||
null!!
|
||||
bar()
|
||||
<!UNREACHABLE_CODE!>bar()<!>
|
||||
}
|
||||
|
||||
fun throwNPE(): Nothing = null!!
|
||||
|
||||
Vendored
+9
-9
@@ -1,40 +1,40 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun testArrayAccess1(array: Array<Any>) {
|
||||
array[todo()]
|
||||
array<!UNREACHABLE_CODE!>[<!>todo()<!UNREACHABLE_CODE!>]<!>
|
||||
}
|
||||
|
||||
fun testArrayAccess2() {
|
||||
operator fun Nothing.get(i: Int, s: String) {}
|
||||
todo()[1, ""]
|
||||
todo()<!UNREACHABLE_CODE!>[1, ""]<!>
|
||||
}
|
||||
|
||||
fun testAraryAccess3() {
|
||||
operator fun Nothing.get(n: Nothing) {}
|
||||
todo()[todo()]
|
||||
todo()<!UNREACHABLE_CODE!>[todo()]<!>
|
||||
}
|
||||
|
||||
fun testArrayAssignment1(array: Array<Any>) {
|
||||
array[todo()] = 11
|
||||
array<!UNREACHABLE_CODE!>[<!>todo()<!UNREACHABLE_CODE!>] = 11<!>
|
||||
}
|
||||
|
||||
fun testArrayAssignment2(array: Array<Any>) {
|
||||
array[1] = todo()
|
||||
array<!UNREACHABLE_CODE!>[<!>1<!UNREACHABLE_CODE!>] =<!> todo()
|
||||
}
|
||||
|
||||
fun testArrayAssignment3(n: Nothing) {
|
||||
operator fun Nothing.set(i: Int, j: Int) {}
|
||||
n[1] = 2
|
||||
n<!UNREACHABLE_CODE!>[1] = 2<!>
|
||||
}
|
||||
|
||||
fun testArrayAssignment4(n: Nothing) {
|
||||
operator fun Nothing.set(i: Int, a: Any) {}
|
||||
n[1] = todo()
|
||||
n<!UNREACHABLE_CODE!>[1] = todo()<!>
|
||||
}
|
||||
|
||||
fun testArrayPlusAssign(array: Array<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()
|
||||
|
||||
Vendored
+6
-6
@@ -1,18 +1,18 @@
|
||||
fun testAssignment() {
|
||||
var a = 1
|
||||
a = todo()
|
||||
var <!UNUSED_VARIABLE!>a<!> = 1
|
||||
<!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>a<!> =<!> todo()
|
||||
}
|
||||
|
||||
fun testVariableDeclaration() {
|
||||
val a = todo()
|
||||
<!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>a<!> =<!> todo()
|
||||
}
|
||||
|
||||
fun testPlusAssign() {
|
||||
operator fun Int.plusAssign(i: Int) {}
|
||||
|
||||
var a = 1
|
||||
a += todo()
|
||||
<!CAN_BE_VAL!>var<!> a = 1
|
||||
a <!UNREACHABLE_CODE!>+=<!> todo()
|
||||
}
|
||||
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
Vendored
+6
-6
@@ -1,14 +1,14 @@
|
||||
fun testBinary1() {
|
||||
operator fun Int.times(s: String) {}
|
||||
|
||||
todo() * ""
|
||||
todo() <!UNREACHABLE_CODE!>* ""<!>
|
||||
}
|
||||
fun testBinary2() {
|
||||
"1" + todo()
|
||||
"1" <!UNREACHABLE_CODE!>+<!> todo()
|
||||
}
|
||||
|
||||
fun testElvis1() {
|
||||
todo() <!USELESS_ELVIS!>?: ""<!>
|
||||
todo() <!UNREACHABLE_CODE, USELESS_ELVIS!>?: ""<!>
|
||||
}
|
||||
|
||||
fun testElvis2(s: String?) {
|
||||
@@ -24,15 +24,15 @@ fun testAnd1(b: Boolean) {
|
||||
}
|
||||
|
||||
fun testAnd2(b: Boolean) {
|
||||
todo() && b
|
||||
todo() && <!UNREACHABLE_CODE!>b<!>
|
||||
}
|
||||
|
||||
fun returnInBinary1(): Boolean {
|
||||
(return true) && (return false)
|
||||
(return true) && (<!UNREACHABLE_CODE!>return false<!>)
|
||||
}
|
||||
|
||||
fun returnInBinary2(): Boolean {
|
||||
(return true) || (return false)
|
||||
(return true) || (<!UNREACHABLE_CODE!>return false<!>)
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
+3
-3
@@ -3,11 +3,11 @@
|
||||
fun testArgumentInCall() {
|
||||
fun bar(i: Int, s: String, x: Any) {}
|
||||
|
||||
bar(1, todo(), "")
|
||||
<!UNREACHABLE_CODE!>bar(<!>1, todo(), <!UNREACHABLE_CODE!>"")<!>
|
||||
}
|
||||
|
||||
fun testArgumentInVariableAsFunctionCall(f: (Any) -> Unit) {
|
||||
f(todo())
|
||||
<!UNREACHABLE_CODE!>f(<!>todo()<!UNREACHABLE_CODE!>)<!>
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
+4
-4
@@ -2,22 +2,22 @@
|
||||
|
||||
fun unreachable0() {
|
||||
return
|
||||
return todo()
|
||||
<!UNREACHABLE_CODE!>return todo()<!>
|
||||
}
|
||||
|
||||
fun unreachable2() {
|
||||
return
|
||||
val a = todo()
|
||||
<!UNREACHABLE_CODE!>val <!UNUSED_VARIABLE!>a<!> = todo()<!>
|
||||
}
|
||||
|
||||
fun unreachable3() {
|
||||
return
|
||||
bar(todo())
|
||||
<!UNREACHABLE_CODE!>bar(todo())<!>
|
||||
}
|
||||
|
||||
fun unreachable4(array: Array<Any>) {
|
||||
return
|
||||
array[todo()]
|
||||
<!UNREACHABLE_CODE!>array[todo()]<!>
|
||||
}
|
||||
|
||||
fun bar(a: Any) {}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fun testIf() {
|
||||
if (todo()) 1 else 2
|
||||
if (todo()) <!UNREACHABLE_CODE!>1<!> else <!UNREACHABLE_CODE!>2<!>
|
||||
}
|
||||
|
||||
fun testIf1(b: Boolean) {
|
||||
|
||||
Vendored
-13
@@ -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()
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun testCompound() {
|
||||
|
||||
Vendored
+5
-5
@@ -1,13 +1,13 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun testObject() {
|
||||
object : Foo(todo()) {
|
||||
object : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
|
||||
fun foo() = 1
|
||||
}
|
||||
}
|
||||
|
||||
fun testObjectExpression() {
|
||||
val a = object : Foo(todo()) {
|
||||
val <!UNUSED_VARIABLE!>a<!> = object : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
|
||||
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 : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {
|
||||
fun foo() = 1
|
||||
})
|
||||
}
|
||||
|
||||
fun testClassDeclaration() {
|
||||
class C : Foo(todo()) {}
|
||||
class C : <!UNREACHABLE_CODE!>Foo(<!>todo()<!UNREACHABLE_CODE!>)<!> {}
|
||||
|
||||
bar()
|
||||
}
|
||||
@@ -33,4 +33,4 @@ fun testFunctionDefaultArgument() {
|
||||
open class Foo(i: Int) {}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
fun bar() {}
|
||||
fun bar() {}
|
||||
|
||||
-21
@@ -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() {}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// FIR_IDENTICAL
|
||||
|
||||
fun testFor() {
|
||||
operator fun Nothing.iterator() = (0..1).iterator()
|
||||
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
fun testReturn() {
|
||||
return todo()
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun testReturn() {
|
||||
<!UNREACHABLE_CODE!>return<!> todo()
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -1,15 +1,15 @@
|
||||
fun testPrefix() {
|
||||
operator fun Any.not() {}
|
||||
!todo()
|
||||
<!UNREACHABLE_CODE!>!<!>todo()
|
||||
}
|
||||
|
||||
fun testPostfixWithCall(n: Nothing) {
|
||||
operator fun Nothing.inc(): Nothing = this
|
||||
<!VAL_REASSIGNMENT!>n<!>++
|
||||
<!VAL_REASSIGNMENT!>n<!><!UNREACHABLE_CODE!>++<!>
|
||||
}
|
||||
|
||||
fun testPostfixSpecial() {
|
||||
todo()<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||
todo()<!UNNECESSARY_NOT_NULL_ASSERTION, UNREACHABLE_CODE!>!!<!>
|
||||
}
|
||||
|
||||
fun todo(): Nothing = throw Exception()
|
||||
|
||||
Vendored
+10
-10
@@ -3,36 +3,36 @@ fun bar(a: Any, b: Any) {}
|
||||
|
||||
fun test(arr: Array<Int>) {
|
||||
while (true) {
|
||||
foo(break)
|
||||
<!UNREACHABLE_CODE!>foo(<!>break<!UNREACHABLE_CODE!>)<!>
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
bar(arr, break)
|
||||
<!UNREACHABLE_CODE!>bar(<!>arr, break<!UNREACHABLE_CODE!>)<!>
|
||||
}
|
||||
|
||||
while (true) {
|
||||
arr[break]
|
||||
arr<!UNREACHABLE_CODE!>[<!>break<!UNREACHABLE_CODE!>]<!>
|
||||
}
|
||||
|
||||
while (true) {
|
||||
arr[1] = break
|
||||
arr<!UNREACHABLE_CODE!>[<!>1<!UNREACHABLE_CODE!>] =<!> break
|
||||
}
|
||||
|
||||
while (true) {
|
||||
break
|
||||
foo(1)
|
||||
<!UNREACHABLE_CODE!>foo(1)<!>
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var x = 1
|
||||
var <!UNUSED_VARIABLE!>x<!> = 1
|
||||
break
|
||||
x = 2
|
||||
<!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>x<!> = 2<!>
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var x = 1
|
||||
x = break
|
||||
var <!UNUSED_VARIABLE!>x<!> = 1
|
||||
<!UNREACHABLE_CODE!><!ASSIGNED_VALUE_IS_NEVER_READ!>x<!> =<!> break
|
||||
}
|
||||
|
||||
// TODO: bug, should be fixed in CFA
|
||||
@@ -50,6 +50,6 @@ fun test(arr: Array<Int>) {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
break <!USELESS_ELVIS!>?: null<!>
|
||||
break <!UNREACHABLE_CODE, USELESS_ELVIS!>?: null<!>
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-12
@@ -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
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
//KT-3162 More precise try-finally error marking
|
||||
|
||||
fun foo(x: String) : String {
|
||||
|
||||
Vendored
-25
@@ -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()
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
//KT-5200 Mark unreachable code in lambdas
|
||||
|
||||
fun test1(): String {
|
||||
|
||||
+3
-3
@@ -2,10 +2,10 @@
|
||||
inline fun myRun(b: () -> Unit) = b()
|
||||
|
||||
fun foo() {
|
||||
var a: Int
|
||||
<!CAN_BE_VAL!>var<!> <!UNUSED_VARIABLE!>a<!>: Int
|
||||
return
|
||||
|
||||
myRun {
|
||||
<!UNREACHABLE_CODE!>myRun {
|
||||
return
|
||||
}
|
||||
}<!>
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,4 +76,4 @@ fun nonLocalReturnAndOrdinaryExit(b: Boolean) {
|
||||
}
|
||||
x.inc()
|
||||
s.length
|
||||
}
|
||||
}
|
||||
|
||||
+4
-7
@@ -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)
|
||||
|
||||
+29
-21
@@ -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<FirDiagnosticCodeMetaInfo> {
|
||||
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) }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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
|
||||
}
|
||||
|
||||
+9
@@ -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"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+13
@@ -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),
|
||||
|
||||
+6
@@ -1819,6 +1819,12 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
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>() {
|
||||
override val diagnosticClass get() = UnsafeCall::class
|
||||
abstract val receiverType: KtType
|
||||
|
||||
+9
@@ -2930,6 +2930,15 @@ internal class InitializationBeforeDeclarationImpl(
|
||||
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(
|
||||
override val receiverType: KtType,
|
||||
override val receiverExpression: KtExpression?,
|
||||
|
||||
Reference in New Issue
Block a user