From 0d4e9ca0b9b3b926ebdb62b3a32b0a7e5c09005f Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Fri, 5 Mar 2021 00:10:36 +0300 Subject: [PATCH] FIR: allow reporting multiple FirDiagnostics from a ConeDiagnostic --- .../ErrorNodeDiagnosticCollectorComponent.kt | 7 +++-- .../coneDiagnosticToFirDiagnostic.kt | 29 ++++++++++++------- .../KtFirAnalysisSessionComponent.kt | 9 ++---- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt index 8d970dd29e2..d787ae649a1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt @@ -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) + } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index a5aa46d1dfd..b6b19999871 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -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? = when (this) { +private fun ConeDiagnostic.toFirDiagnostic(source: FirSourceElement): FirDiagnostic? = when (this) { is ConeUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.on(source, this.name?.asString() ?: "") 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 throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") } +fun ConeDiagnostic.toFirDiagnostics(source: FirSourceElement): List> { + 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> { // 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 { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt index 1c0b26ef2c7..afd655fcf83 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/components/KtFirAnalysisSessionComponent.kt @@ -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 ) -} \ No newline at end of file +}