diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css index 9105fcf9294..6d16af33e75 100644 --- a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css +++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.dark.css @@ -82,4 +82,9 @@ body.dark { .dark .error-counter { color: #ff1010; +} + +.dark .diagnostic-hover { + background-color: #3a3a3a; + color: #dfdfdf; } \ No newline at end of file diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css index 9ce8fcdb204..f6798dcea9c 100644 --- a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css +++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/colors.white.css @@ -21,7 +21,7 @@ } .white .keyword { - color: #8ba558; + color: rgb(203, 159, 52); } .white a.ref.selected { @@ -71,4 +71,9 @@ .white .error-counter { color: #f00; +} + +.white .diagnostic-hover { + background-color: #eaeaea; + color: #000; } \ No newline at end of file diff --git a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css index b16547c6b93..4d7518fc330 100644 --- a/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css +++ b/compiler/fir/dump/resources/org/jetbrains/kotlin/fir/dump/style.css @@ -62,4 +62,29 @@ a.ref { .error-counter { margin-left: 10px; +} + +.error.diagnostic-hover-container { + position: relative; + display: inline-block; +} + +.error:hover .diagnostic-hover { + visibility: visible; + text-decoration: none; + opacity: 1; +} + +.diagnostic-hover { + visibility: hidden; + + border-radius: 6px; + padding: 5px 5px; + position: absolute; + z-index: 1; + bottom: 100%; + left: 0; + + opacity: 0; + transition: opacity 0.3s; } \ No newline at end of file diff --git a/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt b/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt index 5caa6e8d71e..c4caabebc6c 100644 --- a/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt +++ b/compiler/fir/dump/src/org/jetbrains/kotlin/fir/dump/HtmlFirDump.kt @@ -15,12 +15,15 @@ import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor +import org.jetbrains.kotlin.fir.diagnostics.FirDiagnostic import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.diagnostics.FirAmbiguityError +import org.jetbrains.kotlin.fir.resolve.diagnostics.FirInapplicableCandidateError import org.jetbrains.kotlin.fir.symbols.* import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol @@ -31,6 +34,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintError import org.jetbrains.kotlin.types.AbstractStrictEqualityTypeChecker import org.jetbrains.kotlin.types.Variance import java.io.File @@ -575,14 +579,8 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver } } - - private fun FlowContent.generate(klass: FirRegularClass) { - inl() - - declarationStatus(klass.status) - - - when (klass.classKind) { + private fun FlowContent.classKind(kind: ClassKind) { + when (kind) { ClassKind.CLASS -> keyword("class") ClassKind.INTERFACE -> keyword("interface") ClassKind.ENUM_CLASS -> keyword("enum class") @@ -590,6 +588,13 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver ClassKind.ANNOTATION_CLASS -> keyword("annotation class") ClassKind.OBJECT -> keyword("object") } + } + + private fun FlowContent.generate(klass: FirRegularClass) { + inl() + + declarationStatus(klass.status) + classKind(klass.classKind) ws anchoredName(klass.name, klass.classId.asString()) if (klass.superTypeRefs.isNotEmpty()) { @@ -946,6 +951,114 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver } } + private fun FlowContent.describeVerbose(symbol: FirCallableSymbol<*>, fir: FirMemberFunction<*>) { + describeTypeParameters(fir) + + fir.receiverTypeRef?.let { + +"(" + generate(it) + +")." + } + symbolRef(symbol) { + +symbol.callableId.toString() + } + +"(" + generateList(fir.valueParameters) { + generate(it.returnTypeRef) + } + +"): " + generate(fir.returnTypeRef) + } + + private fun FlowContent.describeVerbose(symbol: FirCallableSymbol<*>, fir: FirVariable<*>) { + if (fir is FirTypeParametersOwner) describeTypeParameters(fir) + + fir.receiverTypeRef?.let { + +"(" + generate(it) + +")." + } + symbolRef(symbol) { + +symbol.callableId.toString() + } + +":" + generate(fir.returnTypeRef) + } + + private fun FlowContent.describeTypeParameters(typeParameterContainer: FirTypeParametersOwner) { + if (typeParameterContainer.typeParameters.isEmpty()) return + +"<" + generateList(typeParameterContainer.typeParameters) { + generate(it.variance) + if (it.isReified) { + keyword("reified ") + } + + simpleName(it.name) + + if (it.bounds.isNotEmpty()) { + +": " + generateList(it.bounds) { bound -> + generate(bound) + } + } + } + +"> " + } + + private fun FlowContent.describeVerbose(symbol: FirBasedSymbol<*>) { + when (symbol) { + is FirClassLikeSymbol<*> -> + when (val fir = symbol.fir) { + is FirRegularClass -> { + declarationStatus(fir.status) + classKind(fir.classKind) + ws + symbolRef(symbol) { + +fir.classId.asString() + } + describeTypeParameters(fir) + } + is FirTypeAlias -> { + keyword("typealias ") + symbolRef(symbol) { + +symbol.classId.asString() + } + describeTypeParameters(fir) + } + else -> +symbol.describe() + } + is FirCallableSymbol<*> -> { + when (val fir = symbol.fir) { + is FirSimpleFunction -> { + declarationStatus(fir.status) + keyword("fun ") + describeVerbose(symbol, fir) + } + is FirConstructor -> { + declarationStatus(fir.status) + keyword("constructor ") + describeVerbose(symbol, fir) + } + is FirField -> { + declarationStatus(fir.status) + keyword("field ") + describeVerbose(symbol, fir) + } + is FirProperty -> { + declarationStatus(fir.status) + if (fir.isVal) + keyword("val ") + else if (fir.isVar) + keyword("var ") + describeVerbose(symbol, fir) + } + } + } + else -> +symbol.describe() + } + } + private fun FlowContent.symbolRef(symbol: FirBasedSymbol<*>?, body: FlowContent.() -> Unit) { val (link, classes) = when (symbol) { null -> null to setOf() @@ -960,6 +1073,58 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver } } + private fun FlowContent.diagnosticHover(body: FlowContent.() -> Unit) { + span(classes = "diagnostic-hover") { + body() + } + } + + private fun FlowContent.errorWithDiagnostic(body: FlowContent.() -> Unit) { + error { + classes = classes + "diagnostic-hover-container" + body() + } + } + + private fun FlowContent.generate(diagnostic: FirDiagnostic) { + when (diagnostic) { + is FirInapplicableCandidateError -> { + for (candidate in diagnostic.candidates) { + describeVerbose(candidate.symbol) + br + candidate.diagnostics.forEach { callDiagnostic -> + when (callDiagnostic) { + is NewConstraintError -> { + ident() + +"Lower: " + generate(callDiagnostic.lowerType as ConeKotlinType) + br + ident() + +"Upper: " + generate(callDiagnostic.upperType as ConeKotlinType) + } + else -> { + ident() + callDiagnostic::class.qualifiedName?.let { +it } + Unit + } + } + br + } + } + } + is FirAmbiguityError -> { + +"Ambiguity: " + br + for (candidate in diagnostic.candidates) { + describeVerbose(candidate) + br + } + } + else -> +diagnostic.reason + } + } + private fun FlowContent.generate(reference: FirReference) { when (reference) { is FirSuperReference -> keyword("super") @@ -972,9 +1137,11 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver } } is FirErrorNamedReference -> { - error { - title = reference.diagnostic.reason + errorWithDiagnostic { simpleName(reference.name) + diagnosticHover { + generate(reference.diagnostic) + } } } is FirSimpleNamedReference -> { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 6c6d98ecafc..135331669de 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -349,7 +349,12 @@ class FirCallResolver( applicability < CandidateApplicability.SYNTHETIC_RESOLVED -> { FirErrorNamedReferenceImpl( source, - FirInapplicableCandidateError(applicability, candidates.map { it.symbol }) + FirInapplicableCandidateError(applicability, candidates.map { + FirInapplicableCandidateError.CandidateInfo( + it.symbol, + if (it.systemInitialized) it.system.diagnostics else emptyList() + ) + }) ) } candidates.size == 1 -> { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt index 8defdce804d..cff33319c4d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Candidate.kt @@ -67,9 +67,12 @@ class Candidate( private val baseSystem: ConstraintStorage, val callInfo: CallInfo ) { + + var systemInitialized: Boolean = false val system by lazy { val system = bodyResolveComponents.inferenceComponents.createConstraintSystem() system.addOtherSystem(baseSystem) + systemInitialized = true system } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt index bdfb584c1ec..e6f6237c5e3 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/FirDiagnostics.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic class FirUnresolvedReferenceError(val name: Name? = null) : FirDiagnostic() { override val reason: String get() = "Unresolved reference" + if (name != null) ": ${name.asString()}" else "" @@ -25,8 +26,13 @@ class FirUnresolvedNameError(val name: Name) : FirDiagnostic() { override val reason: String get() = "Unresolved name: $name" } -class FirInapplicableCandidateError(val applicability: CandidateApplicability, val candidates: Collection>) : FirDiagnostic() { - override val reason: String get() = "Inapplicable($applicability): ${candidates.map { describeSymbol(it) }}" +class FirInapplicableCandidateError( + val applicability: CandidateApplicability, + val candidates: Collection +) : FirDiagnostic() { + data class CandidateInfo(val symbol: AbstractFirBasedSymbol<*>, val diagnostics: List) + + override val reason: String get() = "Inapplicable($applicability): ${candidates.map { describeSymbol(it.symbol) }}" } class FirAmbiguityError(val name: Name, val candidates: Collection>) : FirDiagnostic() { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt index 9471bb11ad6..83265a60af9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/diagnostics/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt @@ -53,7 +53,7 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect is FirUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.name?.asString()) is FirUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.classId.asString()) is FirUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.name.asString()) - is FirInapplicableCandidateError -> FirErrors.INAPPLICABLE_CANDIDATE.onSource(source, diagnostic.candidates) + is FirInapplicableCandidateError -> FirErrors.INAPPLICABLE_CANDIDATE.onSource(source, diagnostic.candidates.map { it.symbol }) is FirAmbiguityError -> FirErrors.AMBIGUITY.onSource(source, diagnostic.candidates) is FirOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.onSource(source, diagnostic.candidates) is FirVariableExpectedError -> Errors.VARIABLE_EXPECTED.onSource(source)