[FIR] Support diagnostics in Html dump
This commit is contained in:
@@ -82,4 +82,9 @@ body.dark {
|
|||||||
|
|
||||||
.dark .error-counter {
|
.dark .error-counter {
|
||||||
color: #ff1010;
|
color: #ff1010;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .diagnostic-hover {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
color: #dfdfdf;
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.white .keyword {
|
.white .keyword {
|
||||||
color: #8ba558;
|
color: rgb(203, 159, 52);
|
||||||
}
|
}
|
||||||
|
|
||||||
.white a.ref.selected {
|
.white a.ref.selected {
|
||||||
@@ -71,4 +71,9 @@
|
|||||||
|
|
||||||
.white .error-counter {
|
.white .error-counter {
|
||||||
color: #f00;
|
color: #f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.white .diagnostic-hover {
|
||||||
|
background-color: #eaeaea;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
@@ -62,4 +62,29 @@ a.ref {
|
|||||||
|
|
||||||
.error-counter {
|
.error-counter {
|
||||||
margin-left: 10px;
|
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.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
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.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
|
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression
|
import org.jetbrains.kotlin.fir.expressions.impl.FirUnitExpression
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
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.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
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.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
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.AbstractStrictEqualityTypeChecker
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -575,14 +579,8 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun FlowContent.classKind(kind: ClassKind) {
|
||||||
private fun FlowContent.generate(klass: FirRegularClass) {
|
when (kind) {
|
||||||
inl()
|
|
||||||
|
|
||||||
declarationStatus(klass.status)
|
|
||||||
|
|
||||||
|
|
||||||
when (klass.classKind) {
|
|
||||||
ClassKind.CLASS -> keyword("class")
|
ClassKind.CLASS -> keyword("class")
|
||||||
ClassKind.INTERFACE -> keyword("interface")
|
ClassKind.INTERFACE -> keyword("interface")
|
||||||
ClassKind.ENUM_CLASS -> keyword("enum class")
|
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.ANNOTATION_CLASS -> keyword("annotation class")
|
||||||
ClassKind.OBJECT -> keyword("object")
|
ClassKind.OBJECT -> keyword("object")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FlowContent.generate(klass: FirRegularClass) {
|
||||||
|
inl()
|
||||||
|
|
||||||
|
declarationStatus(klass.status)
|
||||||
|
classKind(klass.classKind)
|
||||||
ws
|
ws
|
||||||
anchoredName(klass.name, klass.classId.asString())
|
anchoredName(klass.name, klass.classId.asString())
|
||||||
if (klass.superTypeRefs.isNotEmpty()) {
|
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) {
|
private fun FlowContent.symbolRef(symbol: FirBasedSymbol<*>?, body: FlowContent.() -> Unit) {
|
||||||
val (link, classes) = when (symbol) {
|
val (link, classes) = when (symbol) {
|
||||||
null -> null to setOf()
|
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) {
|
private fun FlowContent.generate(reference: FirReference) {
|
||||||
when (reference) {
|
when (reference) {
|
||||||
is FirSuperReference -> keyword("super")
|
is FirSuperReference -> keyword("super")
|
||||||
@@ -972,9 +1137,11 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FirErrorNamedReference -> {
|
is FirErrorNamedReference -> {
|
||||||
error {
|
errorWithDiagnostic {
|
||||||
title = reference.diagnostic.reason
|
|
||||||
simpleName(reference.name)
|
simpleName(reference.name)
|
||||||
|
diagnosticHover {
|
||||||
|
generate(reference.diagnostic)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FirSimpleNamedReference -> {
|
is FirSimpleNamedReference -> {
|
||||||
|
|||||||
@@ -349,7 +349,12 @@ class FirCallResolver(
|
|||||||
applicability < CandidateApplicability.SYNTHETIC_RESOLVED -> {
|
applicability < CandidateApplicability.SYNTHETIC_RESOLVED -> {
|
||||||
FirErrorNamedReferenceImpl(
|
FirErrorNamedReferenceImpl(
|
||||||
source,
|
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 -> {
|
candidates.size == 1 -> {
|
||||||
|
|||||||
@@ -67,9 +67,12 @@ class Candidate(
|
|||||||
private val baseSystem: ConstraintStorage,
|
private val baseSystem: ConstraintStorage,
|
||||||
val callInfo: CallInfo
|
val callInfo: CallInfo
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
var systemInitialized: Boolean = false
|
||||||
val system by lazy {
|
val system by lazy {
|
||||||
val system = bodyResolveComponents.inferenceComponents.createConstraintSystem()
|
val system = bodyResolveComponents.inferenceComponents.createConstraintSystem()
|
||||||
system.addOtherSystem(baseSystem)
|
system.addOtherSystem(baseSystem)
|
||||||
|
systemInitialized = true
|
||||||
system
|
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.fir.symbols.impl.FirClassLikeSymbol
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
|
||||||
|
|
||||||
class FirUnresolvedReferenceError(val name: Name? = null) : FirDiagnostic() {
|
class FirUnresolvedReferenceError(val name: Name? = null) : FirDiagnostic() {
|
||||||
override val reason: String get() = "Unresolved reference" + if (name != null) ": ${name.asString()}" else ""
|
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"
|
override val reason: String get() = "Unresolved name: $name"
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirInapplicableCandidateError(val applicability: CandidateApplicability, val candidates: Collection<AbstractFirBasedSymbol<*>>) : FirDiagnostic() {
|
class FirInapplicableCandidateError(
|
||||||
override val reason: String get() = "Inapplicable($applicability): ${candidates.map { describeSymbol(it) }}"
|
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() {
|
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 FirUnresolvedReferenceError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.name?.asString())
|
||||||
is FirUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.classId.asString())
|
is FirUnresolvedSymbolError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.classId.asString())
|
||||||
is FirUnresolvedNameError -> FirErrors.UNRESOLVED_REFERENCE.onSource(source, diagnostic.name.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 FirAmbiguityError -> FirErrors.AMBIGUITY.onSource(source, diagnostic.candidates)
|
||||||
is FirOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.onSource(source, diagnostic.candidates)
|
is FirOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.onSource(source, diagnostic.candidates)
|
||||||
is FirVariableExpectedError -> Errors.VARIABLE_EXPECTED.onSource(source)
|
is FirVariableExpectedError -> Errors.VARIABLE_EXPECTED.onSource(source)
|
||||||
|
|||||||
Reference in New Issue
Block a user