[FIR] Support diagnostics in Html dump
This commit is contained in:
@@ -82,4 +82,9 @@ body.dark {
|
||||
|
||||
.dark .error-counter {
|
||||
color: #ff1010;
|
||||
}
|
||||
|
||||
.dark .diagnostic-hover {
|
||||
background-color: #3a3a3a;
|
||||
color: #dfdfdf;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -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<AbstractFirBasedSymbol<*>>) : FirDiagnostic() {
|
||||
override val reason: String get() = "Inapplicable($applicability): ${candidates.map { describeSymbol(it) }}"
|
||||
class FirInapplicableCandidateError(
|
||||
val applicability: CandidateApplicability,
|
||||
val candidates: Collection<CandidateInfo>
|
||||
) : FirDiagnostic() {
|
||||
data class CandidateInfo(val symbol: AbstractFirBasedSymbol<*>, val diagnostics: List<KotlinCallDiagnostic>)
|
||||
|
||||
override val reason: String get() = "Inapplicable($applicability): ${candidates.map { describeSymbol(it.symbol) }}"
|
||||
}
|
||||
|
||||
class FirAmbiguityError(val name: Name, val candidates: Collection<AbstractFirBasedSymbol<*>>) : FirDiagnostic() {
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user