org.jetbrains.kotlin.diagnostics.rendering.Renderers.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.diagnostics.rendering
import com.google.common.collect.Lists
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.moduleInfo
import org.jetbrains.kotlin.analyzer.unwrapPlatform
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newTable
import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.newText
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRenderer.Companion.DEBUG_TEXT
import org.jetbrains.kotlin.renderer.PropertyAccessorRenderingPolicy
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.MemberComparator
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.getValidityConstraintForConstituentType
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.utils.IDEAPlatforms
import org.jetbrains.kotlin.utils.IDEAPluginsCompatibilityAPI
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.joinToWithBuffer
object Renderers {
private val LOG = Logger.getInstance(Renderers::class.java)
@JvmField
val TO_STRING = Renderer { element ->
if (element is DeclarationDescriptor) {
LOG.warn(
"Diagnostic renderer TO_STRING was used to render an instance of DeclarationDescriptor.\n"
+ "This is usually a bad idea, because descriptors' toString() includes some debug information, "
+ "which should not be seen by the user.\nDescriptor: " + element
)
}
element.toString()
}
@JvmField
val NOT_RENDERED = Renderer { _ -> "" }
@JvmField
val NAME = Renderer { it.name.asString() }
@JvmField
val FQ_NAME = Renderer { it.fqNameSafe.asString() }
@JvmField
val MODULE_WITH_PLATFORM = Renderer { module ->
val platform = module.platform
val moduleName = MODULE.render(module)
val platformNameIfAny = if (platform == null || platform.isCommon()) "" else " for " + platform.single().platformName
moduleName + platformNameIfAny
}
@JvmField
val MODULE = Renderer { module ->
module.moduleInfo?.unwrapPlatform()?.displayedName ?: module.name.asString()
}
@JvmField
val VISIBILITY = Renderer {
it.externalDisplayName
}
@JvmField
val DECLARATION_NAME_WITH_KIND = Renderer {
val name = it.name.asString()
when (it) {
is PackageFragmentDescriptor -> "package '$name'"
is ClassDescriptor -> "${it.renderKind()} '$name'"
is TypeAliasDescriptor -> "typealias '$name'"
is TypeAliasConstructorDescriptor -> "constructor of '${it.typeAliasDescriptor.name.asString()}'"
is ConstructorDescriptor -> "constructor of '${it.constructedClass.name.asString()}'"
is PropertyGetterDescriptor -> "getter of property '${it.correspondingProperty.name.asString()}'"
is PropertySetterDescriptor -> "setter of property '${it.correspondingProperty.name.asString()}'"
is FunctionDescriptor -> "function '$name'"
is PropertyDescriptor -> "property '$name'"
else -> throw AssertionError("Unexpected declaration kind: $it")
}
}
@JvmField
val CAPITALIZED_DECLARATION_NAME_WITH_KIND_AND_PLATFORM = Renderer { descriptor ->
val declarationWithNameAndKind = DECLARATION_NAME_WITH_KIND.render(descriptor)
val withPlatform = if (descriptor is MemberDescriptor && descriptor.isActual)
"actual $declarationWithNameAndKind"
else
declarationWithNameAndKind
withPlatform.replaceFirstChar(Char::uppercaseChar)
}
@JvmField
val NAME_OF_CONTAINING_DECLARATION_OR_FILE = Renderer {
if (DescriptorUtils.isTopLevelDeclaration(it) && it is DeclarationDescriptorWithVisibility && it.visibility == DescriptorVisibilities.PRIVATE) {
"file"
} else {
val containingDeclaration = it.containingDeclaration
if (containingDeclaration is PackageFragmentDescriptor) {
containingDeclaration.fqName.asString().wrapIntoQuotes()
} else {
containingDeclaration!!.name.asString().wrapIntoQuotes()
}
}
}
@JvmField
val ELEMENT_TEXT = Renderer { it.text }
@JvmField
val DECLARATION_NAME = Renderer { it.nameAsSafeName.asString() }
@JvmField
val RENDER_CLASS_OR_OBJECT = Renderer { classOrObject: KtClassOrObject ->
val name = classOrObject.name?.let { " ${it.wrapIntoQuotes()}" } ?: ""
when {
classOrObject !is KtClass -> "Object$name"
classOrObject.isInterface() -> "Interface$name"
else -> "Class$name"
}
}
@JvmField
val RENDER_CLASS_OR_OBJECT_NAME = Renderer { it.renderKindWithName() }
@JvmField
val RENDER_TYPE = SmartTypeRenderer(DescriptorRenderer.FQ_NAMES_IN_TYPES.withOptions {
parameterNamesInFunctionalTypes = false
})
@JvmField
val RENDER_TYPE_WITH_ANNOTATIONS = SmartTypeRenderer(DescriptorRenderer.FQ_NAMES_IN_TYPES_WITH_ANNOTATIONS.withOptions {
parameterNamesInFunctionalTypes = false
})
@JvmField
val TYPE_PROJECTION = Renderer { projection ->
when {
projection.isStarProjection -> "*"
projection.projectionKind == Variance.INVARIANT ->
RENDER_TYPE.render(projection.type, RenderingContext.of(projection.type))
else ->
"${projection.projectionKind} ${RENDER_TYPE.render(projection.type, RenderingContext.of(projection.type))}"
}
}
@JvmField
val AMBIGUOUS_CALLS = Renderer { calls: Collection> ->
val descriptors = calls.map { it.resultingDescriptor }
renderAmbiguousDescriptors(descriptors)
}
@JvmField
val COMPATIBILITY_CANDIDATE = Renderer { call: CallableDescriptor ->
renderAmbiguousDescriptors(listOf(call))
}
@JvmField
val AMBIGUOUS_CALLABLE_REFERENCES = Renderer { references: Collection ->
renderAmbiguousDescriptors(references)
}
private fun renderAmbiguousDescriptors(descriptors: Collection): String {
val context = RenderingContext.Impl(descriptors)
return descriptors
.sortedWith(MemberComparator.INSTANCE)
.joinToString(separator = "\n", prefix = "\n") {
FQ_NAMES_IN_TYPES.render(it, context)
}
}
@JvmStatic
@IDEAPluginsCompatibilityAPI(
IDEAPlatforms._213, // maybe 211 or 212 AS also used it
message = "Please use the CommonRenderers.commaSeparated instead",
plugins = "Android plugin in IDEA"
)
fun commaSeparated(itemRenderer: DiagnosticParameterRenderer): DiagnosticParameterRenderer> =
CommonRenderers.commaSeparated(itemRenderer)
@JvmField
val TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER = Renderer {
renderConflictingSubstitutionsInferenceError(it, TabledDescriptorRenderer.create()).toString()
}
@JvmField
val TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR_RENDERER = Renderer {
renderParameterConstraintError(it, TabledDescriptorRenderer.create()).toString()
}
@JvmField
val TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER = Renderer {
renderNoInformationForParameterError(it, TabledDescriptorRenderer.create()).toString()
}
@JvmField
val TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER = Renderer {
renderUpperBoundViolatedInferenceError(it, TabledDescriptorRenderer.create()).toString()
}
@JvmField
val TYPE_INFERENCE_CANNOT_CAPTURE_TYPES_RENDERER = Renderer {
renderCannotCaptureTypeParameterError(it, TabledDescriptorRenderer.create()).toString()
}
@JvmStatic
fun renderConflictingSubstitutionsInferenceError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
LOG.assertTrue(
inferenceErrorData.constraintSystem.status.hasConflictingConstraints(),
debugMessage("Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData)
)
val substitutedDescriptors = Lists.newArrayList()
val substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(inferenceErrorData.constraintSystem)
for (substitutor in substitutors) {
val substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor)
substitutedDescriptors.add(substitutedDescriptor)
}
val firstConflictingVariable = ConstraintsUtil.getFirstConflictingVariable(inferenceErrorData.constraintSystem)
if (firstConflictingVariable == null) {
LOG.error(debugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData))
return result
}
result.text(
newText()
.normal("Cannot infer type parameter ")
.strong(firstConflictingVariable.name)
.normal(" in ")
)
val table = newTable()
result.table(table)
table.descriptor(inferenceErrorData.descriptor).text("None of the following substitutions")
for (substitutedDescriptor in substitutedDescriptors) {
val receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.extensionReceiverParameter)
val errorPositions = hashSetOf()
val parameterTypes = Lists.newArrayList()
for (valueParameterDescriptor in substitutedDescriptor.valueParameters) {
parameterTypes.add(valueParameterDescriptor.type)
if (valueParameterDescriptor.index >= inferenceErrorData.valueArgumentsTypes.size) continue
val actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.index)
if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(actualType, valueParameterDescriptor.type)) {
errorPositions.add(VALUE_PARAMETER_POSITION.position(valueParameterDescriptor.index))
}
}
if (receiverType != null && inferenceErrorData.receiverArgumentType != null
&& !KotlinTypeChecker.DEFAULT.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
errorPositions.add(RECEIVER_POSITION.position())
}
table.functionArgumentTypeList(receiverType, parameterTypes, { errorPositions.contains(it) })
}
table.text("can be applied to")
.functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes)
return result
}
@JvmStatic
fun renderParameterConstraintError(
inferenceErrorData: InferenceErrorData, renderer: TabledDescriptorRenderer
): TabledDescriptorRenderer {
val constraintErrors = inferenceErrorData.constraintSystem.status.constraintErrors
val errorPositions = constraintErrors.filter { it is ParameterConstraintError }.map { it.constraintPosition }
return renderer.table(
TabledDescriptorRenderer
.newTable()
.descriptor(inferenceErrorData.descriptor)
.text("cannot be applied to")
.functionArgumentTypeList(inferenceErrorData.receiverArgumentType,
inferenceErrorData.valueArgumentsTypes,
{ errorPositions.contains(it) })
)
}
@JvmStatic
fun renderNoInformationForParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
val firstUnknownVariable = inferenceErrorData.constraintSystem.typeVariables.firstOrNull { variable ->
inferenceErrorData.constraintSystem.getTypeBounds(variable).values.isEmpty()
} ?: return result.apply {
LOG.error(debugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData))
}
return result
.text(
newText().normal("Not enough information to infer parameter ")
.strong(firstUnknownVariable.name)
.normal(" in ")
)
.table(
newTable()
.descriptor(inferenceErrorData.descriptor)
.text("Please specify it explicitly.")
)
}
@JvmStatic
fun renderUpperBoundViolatedInferenceError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
val constraintSystem = inferenceErrorData.constraintSystem
val status = constraintSystem.status
LOG.assertTrue(
status.hasViolatedUpperBound(),
debugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)
)
val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(TYPE_BOUND_POSITION)
val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull {
!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, it, inferenceErrorData.call, true)
}
if (typeParameterDescriptor == null) {
if (inferenceErrorData.descriptor is TypeAliasConstructorDescriptor) {
renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(
inferenceErrorData, result, systemWithoutWeakConstraints
)?.let {
return it
}
}
return if (status.hasConflictingConstraints())
renderConflictingSubstitutionsInferenceError(inferenceErrorData, result)
else {
LOG.error(
debugMessage(
"There is no type parameter with violated upper bound for 'upper bound violated' error",
inferenceErrorData,
verbosity = ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE
)
)
result
}
}
val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor)
val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeVariable).value
if (inferredValueForTypeParameter == null) {
LOG.error(
debugMessage(
"System without weak constraints is not successful, there is no value for type parameter " +
typeParameterDescriptor.name + "\n: " + systemWithoutWeakConstraints, inferenceErrorData
)
)
return result
}
result.text(
newText()
.normal("Type parameter bound for ")
.strong(typeParameterDescriptor.name)
.normal(" in ")
)
.table(
newTable()
.descriptor(inferenceErrorData.descriptor)
)
var violatedUpperBound: KotlinType? = null
for (upperBound in typeParameterDescriptor.upperBounds) {
val upperBoundWithSubstitutedInferredTypes =
systemWithoutWeakConstraints.resultingSubstitutor.substitute(upperBound, Variance.INVARIANT)
if (upperBoundWithSubstitutedInferredTypes != null
&& !KotlinTypeChecker.DEFAULT.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
violatedUpperBound = upperBoundWithSubstitutedInferredTypes
break
}
}
if (violatedUpperBound == null) {
LOG.error(
debugMessage(
"Type parameter (chosen as violating its upper bound)" +
typeParameterDescriptor.name + " violates no bounds after substitution", inferenceErrorData
)
)
return result
}
// TODO: context should be in fact shared for the table and these two types
val context = RenderingContext.of(inferredValueForTypeParameter, violatedUpperBound)
val typeRenderer = result.typeRenderer
result.text(
newText()
.normal(" is not satisfied: inferred type ")
.error(typeRenderer.render(inferredValueForTypeParameter, context))
.normal(" is not a subtype of ")
.strong(typeRenderer.render(violatedUpperBound, context))
)
return result
}
private fun renderUpperBoundViolatedInferenceErrorForTypeAliasConstructor(
inferenceErrorData: InferenceErrorData,
result: TabledDescriptorRenderer,
systemWithoutWeakConstraints: ConstraintSystem
): TabledDescriptorRenderer? {
val descriptor = inferenceErrorData.descriptor
if (descriptor !is TypeAliasConstructorDescriptor) {
LOG.error("Type alias constructor descriptor expected: $descriptor")
return result
}
val inferredTypesForTypeParameters = descriptor.typeParameters.map {
val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), it)
systemWithoutWeakConstraints.getTypeBounds(typeVariable).value
}
val inferredTypeSubstitutor = TypeSubstitutor.create(object : TypeConstructorSubstitution() {
override fun get(key: TypeConstructor): TypeProjection? {
val typeDescriptor = key.declarationDescriptor as? TypeParameterDescriptor ?: return null
if (typeDescriptor.containingDeclaration != descriptor.typeAliasDescriptor) return null
return inferredTypesForTypeParameters[typeDescriptor.index]?.let(::TypeProjectionImpl)
}
})
for (constraintError in inferenceErrorData.constraintSystem.status.constraintErrors) {
val constraintInfo = constraintError.constraintPosition.getValidityConstraintForConstituentType() ?: continue
val violatedUpperBound = inferredTypeSubstitutor.safeSubstitute(constraintInfo.bound, Variance.INVARIANT)
val violatingInferredType = inferredTypeSubstitutor.safeSubstitute(constraintInfo.typeArgument, Variance.INVARIANT)
val context = RenderingContext.of(violatingInferredType, violatedUpperBound)
val typeRenderer = result.typeRenderer
result.text(
newText().normal("Type parameter bound for ").strong(constraintInfo.typeParameter.name)
.normal(" in type inferred from type alias expansion for ")
)
.table(newTable().descriptor(inferenceErrorData.descriptor))
result.text(
newText().normal(" is not satisfied: inferred type ").error(typeRenderer.render(violatingInferredType, context))
.normal(" is not a subtype of ").strong(typeRenderer.render(violatedUpperBound, context))
)
return result
}
return null
}
@JvmStatic
fun renderCannotCaptureTypeParameterError(
inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer
): TabledDescriptorRenderer {
val system = inferenceErrorData.constraintSystem
val errors = system.status.constraintErrors
val typeVariableWithCapturedConstraint = errors.firstIsInstanceOrNull()?.typeVariable
if (typeVariableWithCapturedConstraint == null) {
LOG.error(debugMessage("An error 'cannot capture type parameter' is not found in errors", inferenceErrorData))
return result
}
val typeBounds = system.getTypeBounds(typeVariableWithCapturedConstraint)
val boundWithCapturedType = typeBounds.bounds.firstOrNull { it.constrainingType.isCaptured() }
val capturedTypeConstructor = boundWithCapturedType?.constrainingType?.constructor as? CapturedTypeConstructor
if (capturedTypeConstructor == null) {
LOG.error(
debugMessage(
"There is no captured type in bounds, but there is an error 'cannot capture type parameter'",
inferenceErrorData
)
)
return result
}
val typeParameter = typeVariableWithCapturedConstraint.originalTypeParameter
val upperBound = TypeIntersector.getUpperBoundsAsType(typeParameter)
assert(!KotlinBuiltIns.isNullableAny(upperBound) && capturedTypeConstructor.projection.projectionKind == Variance.IN_VARIANCE) {
"There is the only reason to report TYPE_INFERENCE_CANNOT_CAPTURE_TYPES"
}
val explanation =
"Type parameter has an upper bound ${result.typeRenderer.render(
upperBound,
RenderingContext.of(upperBound)
).wrapIntoQuotes()}" +
" that cannot be satisfied capturing 'in' projection"
result.text(
newText().normal(
typeParameter.name.wrapIntoQuotes() +
" cannot capture " +
"${result.typeProjectionRenderer.render(
capturedTypeConstructor.projection,
RenderingContext.of(capturedTypeConstructor.projection)
).wrapIntoQuotes()}. " +
explanation
)
)
return result
}
@JvmField
val CLASSES_OR_SEPARATED = Renderer> { descriptors ->
buildString {
var index = 0
for (descriptor in descriptors) {
append(DescriptorUtils.getFqName(descriptor).asString())
index++
if (index <= descriptors.size - 2) {
append(", ")
} else if (index == descriptors.size - 1) {
append(" or ")
}
}
}
}
private fun renderTypes(
types: Collection,
typeRenderer: DiagnosticParameterRenderer,
context: RenderingContext
): String {
return StringUtil.join(types, { typeRenderer.render(it, context) }, ", ")
}
@JvmField
val RENDER_COLLECTION_OF_TYPES = ContextDependentRenderer> { types, context ->
renderTypes(types, RENDER_TYPE, context)
}
enum class ConstraintSystemRenderingVerbosity {
COMPACT,
DEBUG, // Includes type bounds positions, types are rendered with FQNs instead of short names
EXTRA_VERBOSE // Additionally includes all supertypes of each bounds and type constructors of types
}
fun renderConstraintSystem(constraintSystem: ConstraintSystem, verbosity: ConstraintSystemRenderingVerbosity): String {
val typeBounds = linkedSetOf()
for (variable in constraintSystem.typeVariables) {
typeBounds.add(constraintSystem.getTypeBounds(variable))
}
val separator = if (verbosity == ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE) "\n\n" else "\n"
return "type parameter bounds:\n" +
typeBounds.joinToString(separator = separator) { renderTypeBounds(it, verbosity) } +
"\n\n" +
"status:\n" +
ConstraintsUtil.getDebugMessageForStatus(constraintSystem.status)
}
private fun renderTypeBounds(typeBounds: TypeBounds, verbosity: ConstraintSystemRenderingVerbosity): String {
val renderedTypeVariable = renderTypeVariable(
typeBounds.typeVariable,
includeTypeConstructor = verbosity == ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE
)
return if (typeBounds.bounds.isEmpty()) {
renderedTypeVariable
} else {
// In 'EXTRA_VERBOSE' bounds take a lot of lines and are rendered as a separate block of text
// In other modes they are rendered as a single line
val boundsPrefix = if (verbosity == ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE) "\n" else " "
val boundsSeparator = if (verbosity == ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE) "\n" else ", "
val renderedBounds = typeBounds.bounds.joinToString(separator = boundsSeparator) { renderTypeBound(it, verbosity) }
renderedTypeVariable + boundsPrefix + renderedBounds
}
}
private fun renderTypeVariable(typeVariable: TypeVariable, includeTypeConstructor: Boolean): String {
val typeVariableName = typeVariable.name.asString()
if (!includeTypeConstructor) return typeVariableName
return "TypeVariable $typeVariableName, " +
"descriptor = ${typeVariable.freshTypeParameter}, " +
"typeConstructor = ${renderTypeConstructor(typeVariable.freshTypeParameter.typeConstructor)}"
}
private fun renderTypeBound(bound: Bound, verbosity: ConstraintSystemRenderingVerbosity): String {
val typeRendered = if (verbosity == ConstraintSystemRenderingVerbosity.COMPACT)
DescriptorRenderer.SHORT_NAMES_IN_TYPES
else
DescriptorRenderer.FQ_NAMES_IN_TYPES
val arrow = when (bound.kind) {
LOWER_BOUND -> ">: "
UPPER_BOUND -> "<: "
else -> ":= "
}
val initialBoundRender = arrow + typeRendered.renderType(bound.constrainingType) + if (!bound.isProper) "*" else ""
return when (verbosity) {
ConstraintSystemRenderingVerbosity.COMPACT -> initialBoundRender
ConstraintSystemRenderingVerbosity.DEBUG -> "$initialBoundRender (${bound.position}) "
ConstraintSystemRenderingVerbosity.EXTRA_VERBOSE -> {
"$initialBoundRender (${bound.position})\n" +
"Constraining type additional info: ${renderTypeConstructor(bound.constrainingType.constructor)}\n" +
"Supertypes of constraining type:\n" +
TypeUtils.getAllSupertypes(bound.constrainingType).joinToString("\n") {
"- " + typeRendered.renderType(it) + ", TypeConstructor info: ${renderTypeConstructor(it.constructor)}"
} +
"\n"
}
}
}
@Suppress("deprecation")
private fun renderTypeConstructor(typeConstructor: TypeConstructor): String {
return "$typeConstructor[${typeConstructor.javaClass.name}], " +
"${(typeConstructor as? AbstractTypeConstructor)?.renderAdditionalDebugInformation()}"
}
private fun debugMessage(
message: String,
inferenceErrorData: InferenceErrorData,
verbosity: ConstraintSystemRenderingVerbosity = ConstraintSystemRenderingVerbosity.DEBUG
) = buildString {
append(message)
append("\nConstraint system: \n")
append(renderConstraintSystem(inferenceErrorData.constraintSystem, verbosity))
append("\nDescriptor:\n")
append(inferenceErrorData.descriptor)
append("\nExpected type:\n")
val context = RenderingContext.Empty
if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
append(inferenceErrorData.expectedType)
} else {
append(RENDER_TYPE_WITH_ANNOTATIONS.render(inferenceErrorData.expectedType, context))
}
append("\nArgument types:\n")
if (inferenceErrorData.receiverArgumentType != null) {
append(RENDER_TYPE_WITH_ANNOTATIONS.render(inferenceErrorData.receiverArgumentType, context)).append(".")
}
append("(").append(renderTypes(inferenceErrorData.valueArgumentsTypes, RENDER_TYPE_WITH_ANNOTATIONS, context)).append(")")
}
private fun String.wrapIntoQuotes(): String = "'$this'"
private fun Name.wrapIntoQuotes(): String = "'${this.asString()}'"
private val WHEN_MISSING_LIMIT = 7
private val List.assumesElseBranchOnly: Boolean
get() = any { it == WhenMissingCase.Unknown || it is WhenMissingCase.ConditionTypeIsExpect }
@JvmField
val RENDER_WHEN_MISSING_CASES = Renderer> {
if (!it.assumesElseBranchOnly) {
val list = it.joinToString(", ", limit = WHEN_MISSING_LIMIT) { "'$it'" }
val branches = if (it.size > 1) "branches" else "branch"
"$list $branches or 'else' branch instead"
} else {
"'else' branch"
}
}
@JvmField
val FQ_NAMES_IN_TYPES = DescriptorRenderer.FQ_NAMES_IN_TYPES.asRenderer()
@JvmField
val FQ_NAMES_IN_TYPES_ANNOTATIONS_WHITELIST = DescriptorRenderer.FQ_NAMES_IN_TYPES_WITH_ANNOTATIONS.withAnnotationsWhitelist()
@JvmField
val FQ_NAMES_IN_TYPES_WITH_ANNOTATIONS = DescriptorRenderer.FQ_NAMES_IN_TYPES_WITH_ANNOTATIONS.asRenderer()
@JvmField
val COMPACT = DescriptorRenderer.COMPACT.asRenderer()
@JvmField
val COMPACT_WITHOUT_SUPERTYPES = DescriptorRenderer.COMPACT_WITHOUT_SUPERTYPES.asRenderer()
@JvmField
val WITHOUT_MODIFIERS = DescriptorRenderer.WITHOUT_MODIFIERS.asRenderer()
@JvmField
val SHORT_NAMES_IN_TYPES = DescriptorRenderer.SHORT_NAMES_IN_TYPES.asRenderer()
@JvmField
val COMPACT_WITH_MODIFIERS = DescriptorRenderer.COMPACT_WITH_MODIFIERS.asRenderer()
@JvmField
val DEPRECATION_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions {
withoutTypeParameters = false
receiverAfterName = false
propertyAccessorRenderingPolicy = PropertyAccessorRenderingPolicy.PRETTY
}.asRenderer()
@JvmField
val DESCRIPTORS_ON_NEWLINE_WITH_INDENT = object : DiagnosticParameterRenderer> {
private val mode = MultiplatformDiagnosticRenderingMode()
override fun render(obj: Collection, renderingContext: RenderingContext): String {
return buildString {
for (descriptor in obj) {
mode.newLine(this)
mode.renderDescriptor(this, descriptor, renderingContext, "")
}
}
}
}
fun renderExpressionType(type: KotlinType?, dataFlowTypes: Set?): String {
if (type == null)
return "Type is unknown"
if (dataFlowTypes == null)
return DEBUG_TEXT.renderType(type)
val typesAsString = dataFlowTypes.map { DEBUG_TEXT.renderType(it) }.toMutableSet().apply { add(DEBUG_TEXT.renderType(type)) }
return typesAsString.sorted().joinToString(separator = " & ")
}
fun renderCallInfo(fqName: FqNameUnsafe?, typeCall: String) =
buildString {
append("fqName: ${fqName?.asString() ?: "fqName is unknown"}; ")
append("typeCall: $typeCall")
}
}
fun DescriptorRenderer.asRenderer() = SmartDescriptorRenderer(this)