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 67af299d2ff..e8ad06d1cff 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 @@ -6,26 +6,20 @@ package org.jetbrains.kotlin.fir.analysis.collectors.components import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.fir.FirFakeSourceElementKind 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.FirDiagnostic -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory0 -import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.toFirDiagnostic import org.jetbrains.kotlin.fir.declarations.FirErrorFunction -import org.jetbrains.kotlin.fir.diagnostics.* -import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind.* +import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.expressions.FirErrorExpression import org.jetbrains.kotlin.fir.expressions.FirErrorLoop import org.jetbrains.kotlin.fir.expressions.FirErrorResolvedQualifier import org.jetbrains.kotlin.fir.references.FirErrorNamedReference -import org.jetbrains.kotlin.fir.resolve.calls.InapplicableWrongReceiver -import org.jetbrains.kotlin.fir.resolve.diagnostics.* -import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.resolve.calls.tower.isSuccess -import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError +import org.jetbrains.kotlin.fir.types.FirErrorTypeRef class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollector) : AbstractDiagnosticCollectorComponent(collector) { override fun visitErrorLoop(errorLoop: FirErrorLoop, data: CheckerContext) { @@ -77,95 +71,7 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect } } - val coneDiagnostic = when (diagnostic) { - is ConeUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.on(source, diagnostic.name?.asString() ?: "") - is ConeUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.on(source, diagnostic.classId.asString()) - is ConeUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.on(source, diagnostic.name.asString()) - is ConeHiddenCandidateError -> FirErrors.HIDDEN.on(source, diagnostic.candidateSymbol) - is ConeInapplicableCandidateError -> mapInapplicableCandidateError(diagnostic, source) - is ConeAmbiguityError -> if (!diagnostic.applicability.isSuccess) { - FirErrors.NONE_APPLICABLE.on(source, diagnostic.candidates) - } else { - FirErrors.AMBIGUITY.on(source, diagnostic.candidates) - } - is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.on(source, diagnostic.candidates) - is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.on(source) - is ConeTypeMismatchError -> FirErrors.TYPE_MISMATCH.on(source, diagnostic.expectedType, diagnostic.actualType) - is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.on(diagnostic.source.safeAs() ?: source) - is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.on(source, diagnostic.name.asString()) - is ConeWrongNumberOfTypeArgumentsError -> - FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(source, diagnostic.desiredCount, diagnostic.type) - is ConeSimpleDiagnostic -> when { - source.kind is FirFakeSourceElementKind -> null - diagnostic.kind == SymbolNotFound -> FirErrors.UNRESOLVED_REFERENCE.on(source, "") - else -> diagnostic.getFactory().on(source) - } - is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, diagnostic.target) - is ConeStubDiagnostic -> null - is ConeIntermediateDiagnostic -> null - else -> throw IllegalArgumentException("Unsupported diagnostic type: ${diagnostic.javaClass}") - } + val coneDiagnostic = diagnostic.toFirDiagnostic(source) reporter.report(coneDiagnostic, context) } - - private fun ConeKotlinType.isEffectivelyNotNull(): Boolean { - return when (this) { - is ConeClassLikeType -> !isMarkedNullable - is ConeTypeParameterType -> !isMarkedNullable && lookupTag.typeParameterSymbol.fir.bounds.any { - it.coneTypeSafe()?.isEffectivelyNotNull() == true - } - else -> false - } - } - - private fun mapInapplicableCandidateError( - diagnostic: ConeInapplicableCandidateError, - source: FirSourceElement, - ): FirDiagnostic<*> { - // TODO: Need to distinguish SMARTCAST_IMPOSSIBLE - // TODO: handle other UNSAFE_* variants: invoke, infix, operator - val rootCause = diagnostic.diagnostics.find { it.applicability == diagnostic.applicability } - if (rootCause != null && - rootCause is InapplicableWrongReceiver && - rootCause.actualType?.isNullable == true && - (rootCause.expectedType == null || !rootCause.expectedType!!.isMarkedNullable) - ) { - val expectedType = rootCause.expectedType - - if (expectedType == null || expectedType.isEffectivelyNotNull()) { - return FirErrors.UNSAFE_CALL.on(source, rootCause.actualType!!) - } - } - return FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidateSymbol) - } - - private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0 { - @Suppress("UNCHECKED_CAST") - return when (kind) { - Syntax -> FirErrors.SYNTAX - ReturnNotAllowed -> FirErrors.RETURN_NOT_ALLOWED - UnresolvedLabel -> FirErrors.UNRESOLVED_LABEL - NoThis -> FirErrors.NO_THIS - IllegalConstExpression -> FirErrors.ILLEGAL_CONST_EXPRESSION - IllegalUnderscore -> FirErrors.ILLEGAL_UNDERSCORE - DeserializationError -> FirErrors.DESERIALIZATION_ERROR - InferenceError -> FirErrors.INFERENCE_ERROR - TypeParameterAsSupertype -> FirErrors.TYPE_PARAMETER_AS_SUPERTYPE - EnumAsSupertype -> FirErrors.ENUM_AS_SUPERTYPE - RecursionInSupertypes -> FirErrors.RECURSION_IN_SUPERTYPES - RecursionInImplicitTypes -> FirErrors.RECURSION_IN_IMPLICIT_TYPES - Java -> FirErrors.ERROR_FROM_JAVA_RESOLUTION - SuperNotAllowed -> FirErrors.SUPER_IS_NOT_AN_EXPRESSION - ExpressionRequired -> FirErrors.EXPRESSION_REQUIRED - JumpOutsideLoop -> FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP - NotLoopLabel -> FirErrors.NOT_A_LOOP_LABEL - VariableExpected -> FirErrors.VARIABLE_EXPECTED - NoTypeForTypeParameter -> FirErrors.NO_TYPE_FOR_TYPE_PARAMETER - UnknownCallableKind -> FirErrors.UNKNOWN_CALLABLE_KIND - IllegalProjectionUsage -> FirErrors.ILLEGAL_PROJECTION_USAGE - MissingStdlibClass -> FirErrors.MISSING_STDLIB_CLASS - Other -> FirErrors.OTHER_ERROR - else -> throw IllegalArgumentException("Unsupported diagnostic kind: $kind at $javaClass") - } - } } 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 new file mode 100644 index 00000000000..cf172a497b5 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -0,0 +1,105 @@ +/* + * 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 org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.diagnostics.* +import org.jetbrains.kotlin.fir.resolve.calls.InapplicableWrongReceiver +import org.jetbrains.kotlin.fir.resolve.diagnostics.* +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +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 { + FirErrors.AMBIGUITY.on(source, this.candidates) + } + is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.on(source, this.candidates) + is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.on(source) + is ConeTypeMismatchError -> FirErrors.TYPE_MISMATCH.on(source, this.expectedType, this.actualType) + is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.on(this.source.safeAs() ?: source) + is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.on(source, this.name.asString()) + is ConeWrongNumberOfTypeArgumentsError -> + FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(source, this.desiredCount, this.type) + is ConeSimpleDiagnostic -> when { + source.kind is FirFakeSourceElementKind -> null + this.kind == DiagnosticKind.SymbolNotFound -> FirErrors.UNRESOLVED_REFERENCE.on(source, "") + else -> this.getFactory().on(source) + } + is ConeInstanceAccessBeforeSuperCall -> FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL.on(source, this.target) + is ConeStubDiagnostic -> null + is ConeIntermediateDiagnostic -> null + else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") +} + +private fun ConeKotlinType.isEffectivelyNotNull(): Boolean { + return when (this) { + is ConeClassLikeType -> !isMarkedNullable + is ConeTypeParameterType -> !isMarkedNullable && lookupTag.typeParameterSymbol.fir.bounds.any { + it.coneTypeSafe()?.isEffectivelyNotNull() == true + } + else -> false + } +} + +private fun mapInapplicableCandidateError( + diagnostic: ConeInapplicableCandidateError, + source: FirSourceElement, +): FirDiagnostic<*> { + // TODO: Need to distinguish SMARTCAST_IMPOSSIBLE + // TODO: handle other UNSAFE_* variants: invoke, infix, operator + val rootCause = diagnostic.diagnostics.find { it.applicability == diagnostic.applicability } + if (rootCause != null && + rootCause is InapplicableWrongReceiver && + rootCause.actualType?.isNullable == true && + (rootCause.expectedType == null || !rootCause.expectedType!!.isMarkedNullable) + ) { + val expectedType = rootCause.expectedType + + if (expectedType == null || expectedType.isEffectivelyNotNull()) { + return FirErrors.UNSAFE_CALL.on(source, rootCause.actualType!!) + } + } + return FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidateSymbol) +} + +private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0 { + @Suppress("UNCHECKED_CAST") + return when (kind) { + DiagnosticKind.Syntax -> FirErrors.SYNTAX + DiagnosticKind.ReturnNotAllowed -> FirErrors.RETURN_NOT_ALLOWED + DiagnosticKind.UnresolvedLabel -> FirErrors.UNRESOLVED_LABEL + DiagnosticKind.NoThis -> FirErrors.NO_THIS + DiagnosticKind.IllegalConstExpression -> FirErrors.ILLEGAL_CONST_EXPRESSION + DiagnosticKind.IllegalUnderscore -> FirErrors.ILLEGAL_UNDERSCORE + DiagnosticKind.DeserializationError -> FirErrors.DESERIALIZATION_ERROR + DiagnosticKind.InferenceError -> FirErrors.INFERENCE_ERROR + DiagnosticKind.TypeParameterAsSupertype -> FirErrors.TYPE_PARAMETER_AS_SUPERTYPE + DiagnosticKind.EnumAsSupertype -> FirErrors.ENUM_AS_SUPERTYPE + DiagnosticKind.RecursionInSupertypes -> FirErrors.RECURSION_IN_SUPERTYPES + DiagnosticKind.RecursionInImplicitTypes -> FirErrors.RECURSION_IN_IMPLICIT_TYPES + DiagnosticKind.Java -> FirErrors.ERROR_FROM_JAVA_RESOLUTION + DiagnosticKind.SuperNotAllowed -> FirErrors.SUPER_IS_NOT_AN_EXPRESSION + DiagnosticKind.ExpressionRequired -> FirErrors.EXPRESSION_REQUIRED + DiagnosticKind.JumpOutsideLoop -> FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP + DiagnosticKind.NotLoopLabel -> FirErrors.NOT_A_LOOP_LABEL + DiagnosticKind.VariableExpected -> FirErrors.VARIABLE_EXPECTED + DiagnosticKind.NoTypeForTypeParameter -> FirErrors.NO_TYPE_FOR_TYPE_PARAMETER + DiagnosticKind.UnknownCallableKind -> FirErrors.UNKNOWN_CALLABLE_KIND + DiagnosticKind.IllegalProjectionUsage -> FirErrors.ILLEGAL_PROJECTION_USAGE + DiagnosticKind.MissingStdlibClass -> FirErrors.MISSING_STDLIB_CLASS + DiagnosticKind.Other -> FirErrors.OTHER_ERROR + else -> throw IllegalArgumentException("Unsupported diagnostic kind: $kind at $javaClass") + } +} \ No newline at end of file