FIR: allow reporting multiple FirDiagnostics from a ConeDiagnostic

This commit is contained in:
Tianyu Geng
2021-03-05 00:10:36 +03:00
committed by Mikhail Glukhikh
parent 1f15ce2d26
commit 0d4e9ca0b9
3 changed files with 25 additions and 20 deletions
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostics
import org.jetbrains.kotlin.fir.declarations.FirErrorFunction
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirErrorExpression
@@ -77,7 +77,8 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect
if (source.kind == FirFakeSourceElementKind.ImplicitConstructor) {
return
}
val coneDiagnostic = diagnostic.toFirDiagnostic(source)
reporter.report(coneDiagnostic, context)
for (coneDiagnostic in diagnostic.toFirDiagnostics(source)) {
reporter.report(coneDiagnostic, context)
}
}
}
@@ -21,13 +21,13 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.ifEmpty
fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnostic<FirSourceElement>? = when (this) {
private fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnostic<FirSourceElement>? = when (this) {
is ConeUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name?.asString() ?: "<No name>")
is ConeUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.classId.asString())
is ConeUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name.asString())
is ConeHiddenCandidateError -> FirErrors.HIDDEN.on(source, this.candidateSymbol)
is ConeInapplicableCandidateError -> mapInapplicableCandidateError(this, source)
is ConeAmbiguityError -> if (!this.applicability.isSuccess) {
FirErrors.NONE_APPLICABLE.on(source, this.candidates)
} else {
@@ -53,6 +53,13 @@ fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnostic<FirS
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
}
fun ConeDiagnostic.toFirDiagnostics(source: FirSourceElement): List<FirDiagnostic<FirSourceElement>> {
if (this is ConeInapplicableCandidateError) {
return mapInapplicableCandidateError(this, source)
}
return listOfNotNull(toFirDiagnostic(source))
}
private fun ConeKotlinType.isEffectivelyNotNull(): Boolean {
return when (this) {
is ConeClassLikeType -> !isMarkedNullable
@@ -68,8 +75,7 @@ private fun mapUnsafeCallError(
source: FirSourceElement,
rootCause: ResolutionDiagnostic?,
): FirDiagnostic<*>? {
if (rootCause != null &&
rootCause is InapplicableWrongReceiver &&
if (rootCause is InapplicableWrongReceiver &&
rootCause.actualType?.isNullable == true &&
(rootCause.expectedType == null || rootCause.expectedType!!.isEffectivelyNotNull())
) {
@@ -102,15 +108,16 @@ private fun mapUnsafeCallError(
private fun mapInapplicableCandidateError(
diagnostic: ConeInapplicableCandidateError,
source: FirSourceElement,
): FirDiagnostic<*> {
): List<FirDiagnostic<FirSourceElement>> {
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
val rootCause = diagnostic.candidate.diagnostics.find { it.applicability == diagnostic.applicability }
mapUnsafeCallError(diagnostic, source, rootCause)?.let { return it }
return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
mapUnsafeCallError(diagnostic, source, rootCause)?.let { return@mapNotNull it }
return when (rootCause) {
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(rootCause.argument.source ?: source)
else -> FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)
}
when (rootCause) {
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(rootCause.argument.source ?: source)
else -> null
}
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
}
private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0<FirSourceElement, *> {
@@ -9,14 +9,11 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirPsiDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostics
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeCheckerContext
import org.jetbrains.kotlin.idea.frontend.api.ValidityTokenOwner
import org.jetbrains.kotlin.idea.frontend.api.components.KtAnalysisSessionComponent
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KT_DIAGNOSTIC_CONVERTER
@@ -34,7 +31,7 @@ internal interface KtFirAnalysisSessionComponent {
KT_DIAGNOSTIC_CONVERTER.convert(analysisSession, this as FirDiagnostic<*>)
fun ConeDiagnostic.asKtDiagnostic(source: FirSourceElement): KtDiagnosticWithPsi<*>? {
val firDiagnostic = toFirDiagnostic(source) ?: return null
val firDiagnostic = toFirDiagnostics(source).firstOrNull() ?: return null
check(firDiagnostic is FirPsiDiagnostic<*>)
return firDiagnostic.asKtDiagnostic()
}
@@ -44,4 +41,4 @@ internal interface KtFirAnalysisSessionComponent {
isStubTypeEqualsToAnything = true,
analysisSession.firResolveState.rootModuleSession.typeContext //TODO use correct session here
)
}
}