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