提交 18bde2c5 编写于 作者: D Dmitriy Novozhilov

[FIR] Reimplement when exhaustiveness checker to fir it's logic with FE 1.0

上级 2a1c9283
......@@ -107,10 +107,10 @@ FILE: exhaustiveness_sealedSubClass.kt
}
}
.<Unresolved name: plus>#(Int(0))
lval d: R|ERROR CLASS: Unresolved name: plus| = when (R|<local>/e|) {
lval d: R|kotlin/Int| = when (R|<local>/e|) {
($subj$ is R|C|) -> {
Int(1)
}
}
.<Unresolved name: plus>#(Int(0))
.R|kotlin/Int.plus|(Int(0))
}
......@@ -48,7 +48,7 @@ fun test_2(e: A) {
is D -> 2
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
val d = <!NO_ELSE_IN_WHEN!>when<!> (e) {
val d = when (e) {
is C -> 1
}.<!UNRESOLVED_REFERENCE{LT}!><!UNRESOLVED_REFERENCE{PSI}!>plus<!>(0)<!>
}.plus(0)
}
......@@ -11,19 +11,19 @@ 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.ExhaustivenessStatus
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.expressions.isExhaustive
object FirExhaustiveWhenChecker : FirWhenExpressionChecker() {
override fun check(expression: FirWhenExpression, context: CheckerContext, reporter: DiagnosticReporter) {
// TODO: add reporting of proper missing clauses, see class WhenMissingCase
if (expression.usedAsExpression && !expression.isExhaustive) {
val factory = if (expression.source?.isIfExpression == true) {
FirErrors.INVALID_IF_AS_EXPRESSION
if (expression.source?.isIfExpression == true) {
reporter.reportOn(expression.source, FirErrors.INVALID_IF_AS_EXPRESSION, context)
} else {
FirErrors.NO_ELSE_IN_WHEN
val missingCases = (expression.exhaustivenessStatus as ExhaustivenessStatus.NotExhaustive).reasons
reporter.reportOn(expression.source, FirErrors.NO_ELSE_IN_WHEN, missingCases, context)
}
reporter.reportOn(expression.source, factory, context)
}
}
......
......@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMB
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.TO_STRING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VISIBILITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.WHEN_MISSING_CASES
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_DELEGATED_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_FUNCTION_WITH_BODY
......@@ -504,7 +505,7 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
//)
// When expressions
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive")
map.put(NO_ELSE_IN_WHEN, "''when'' expression must be exhaustive, add necessary {0}", WHEN_MISSING_CASES)
map.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression")
// Extended checkers group
......
......@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirRenderer
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
......@@ -88,4 +89,16 @@ object FirDiagnosticRenderers {
SYMBOL.render(symbol)
}
}
private const val WHEN_MISSING_LIMIT = 7
val WHEN_MISSING_CASES = Renderer { missingCases: List<WhenMissingCase> ->
if (missingCases.firstOrNull() == WhenMissingCase.Unknown) {
"'else' branch"
} else {
val list = missingCases.joinToString(", ", limit = WHEN_MISSING_LIMIT) { "'$it'" }
val branches = if (missingCases.size > 1) "branches" else "branch"
"$list $branches or 'else' branch instead"
}
}
}
......@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
import org.jetbrains.kotlin.fir.expressions.WhenMissingCase
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
......@@ -231,7 +232,7 @@ object FirErrors {
// TODO: val UNEXPECTED_SAFE_CALL by ...
// When expressions
val NO_ELSE_IN_WHEN by error0<FirSourceElement, KtWhenExpression>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
val NO_ELSE_IN_WHEN by error1<FirSourceElement, KtWhenExpression, List<WhenMissingCase>>(SourceElementPositioningStrategies.WHEN_EXPRESSION)
val INVALID_IF_AS_EXPRESSION by error0<FirSourceElement, KtIfExpression>(SourceElementPositioningStrategies.IF_EXPRESSION)
// Extended checkers group
......
......@@ -5,19 +5,61 @@
package org.jetbrains.kotlin.fir.expressions
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.name.ClassId
sealed class ExhaustivenessStatus {
object Exhaustive : ExhaustivenessStatus()
class NotExhaustive(val reasons: List<WhenMissingCase>) : ExhaustivenessStatus()
class NotExhaustive(val reasons: List<WhenMissingCase>) : ExhaustivenessStatus() {
companion object {
val NO_ELSE_BRANCH = NotExhaustive(listOf(WhenMissingCase.Unknown))
}
}
}
sealed class WhenMissingCase {
object Unknown : WhenMissingCase()
object NullIsMissing : WhenMissingCase()
class BooleanIsMissing(val value: Boolean) : WhenMissingCase()
class IsTypeCheckIsMissing(val classId: ClassId) : WhenMissingCase()
class EnumCheckIsMissing(val classId: ClassId) : WhenMissingCase()
sealed class WhenMissingCase() {
abstract val branchConditionText: String
object Unknown : WhenMissingCase() {
override fun toString(): String = "unknown"
override val branchConditionText: String = "else"
}
object NullIsMissing : WhenMissingCase() {
override val branchConditionText: String = "null"
}
sealed class BooleanIsMissing(val value: Boolean) : WhenMissingCase() {
object True : BooleanIsMissing(true)
object False : BooleanIsMissing(false)
override val branchConditionText: String = value.toString()
}
class IsTypeCheckIsMissing(val classId: ClassId, val isSingleton: Boolean) : WhenMissingCase() {
override val branchConditionText: String = run {
val fqName = classId.asSingleFqName().toString()
if (isSingleton) fqName else "is $fqName"
}
override fun toString(): String {
val name = classId.shortClassName.identifier
return if (isSingleton) name else "is $name"
}
}
class EnumCheckIsMissing(val callableId: CallableId) : WhenMissingCase() {
override val branchConditionText: String = callableId.asFqNameForDebugInfo().toString()
override fun toString(): String {
return callableId.callableName.identifier
}
}
override fun toString(): String {
return branchConditionText
}
}
val FirWhenExpression.isExhaustive: Boolean
......
......@@ -18,6 +18,6 @@ fun test2(x: Stmt): String =
}
fun test3(x: Expr): String =
<!NO_ELSE_IN_WHEN!>when<!> (x) {
when (x) {
is Stmt -> "stmt"
}
sealed class Base {
sealed class A : Base() {
object A1 : A()
sealed class A2 : A()
}
sealed class B : Base() {
sealed class B1 : B()
object B2 : B()
}
fun foo() = when (this) {
is A -> 1
is B.B1 -> 2
B.B2 -> 3
// No else required
}
fun bar() = <!NO_ELSE_IN_WHEN!>when<!> (this) {
is A -> 1
is B.B1 -> 2
}
fun baz() = when (this) {
is A -> 1
B.B2 -> 3
// No else required (no possible B1 instances)
}
fun negated() = when (this) {
!is A -> 1
A.A1 -> 2
is A.A2 -> 3
}
}
// FIR_IDENTICAL
sealed class Base {
sealed class A : Base() {
object A1 : A()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册