[FIR] Support diagnostics in Html dump

This commit is contained in:
Simon Ogorodnik
2019-11-14 17:11:47 +03:00
parent 6e8f8f9a65
commit 9249d1b727
8 changed files with 231 additions and 15 deletions
@@ -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
}
@@ -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() {
@@ -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)