Adjust diagnostics rendering so that short classifier names are rendered unless the name is ambiguous in corresponding RenderingContext

This commit is contained in:
Pavel V. Talanov
2016-02-17 16:40:55 +03:00
parent c7cb596f74
commit e4cf090720
7 changed files with 164 additions and 25 deletions
@@ -109,7 +109,7 @@ object Renderers {
@JvmField val RENDER_CLASS_OR_OBJECT_NAME = Renderer<ClassDescriptor> { it.renderKindWithName() }
@JvmField val RENDER_TYPE = Renderer<KotlinType> { DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(it) }
@JvmField val RENDER_TYPE = SmartTypeRenderer(DescriptorRenderer.FQ_NAMES_IN_TYPES)
@JvmField val RENDER_POSITION_VARIANCE = Renderer {
variance: Variance ->
@@ -442,6 +442,4 @@ object Renderers {
}.asRenderer()
}
fun DescriptorRenderer.asRenderer() = Renderer<DeclarationDescriptor> {
render(it)
}
fun DescriptorRenderer.asRenderer() = SmartDescriptorRenderer(this)
@@ -0,0 +1,105 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.contains
import java.util.*
val RenderingContext.adaptiveClassifierPolicy: ClassifierNamePolicy
get() = this[ADAPTIVE_CLASSIFIER_POLICY_KEY]
private class AdaptiveClassifierNamePolicy(private val ambiguousNames: List<Name>) : ClassifierNamePolicy {
private val renderedParameters = mutableMapOf<Name, LinkedHashSet<TypeParameterDescriptor>>()
override fun renderClassifier(classifier: ClassifierDescriptor, renderer: DescriptorRenderer): String {
return when {
hasUniqueName(classifier) -> ClassifierNamePolicy.SHORT.renderClassifier(classifier, renderer)
classifier is ClassDescriptor -> ClassifierNamePolicy.FULLY_QUALIFIED.renderClassifier(classifier, renderer)
classifier is TypeParameterDescriptor -> {
val name = classifier.name
val typeParametersWithSameName = renderedParameters.getOrPut(name) { LinkedHashSet() }
val isFirstOccurence = typeParametersWithSameName.add(classifier)
val index = typeParametersWithSameName.indexOf(classifier)
renderer.renderAmbiguousTypeParameter(classifier, index + 1, isFirstOccurence)
}
else -> error("Unexpected classifier: ${classifier.javaClass}")
}
}
private fun hasUniqueName(classifier: ClassifierDescriptor): Boolean {
return classifier.name !in ambiguousNames
}
private fun DescriptorRenderer.renderAmbiguousTypeParameter(
typeParameter: TypeParameterDescriptor, index: Int, firstOccurence: Boolean
) = buildString {
append(typeParameter.name)
append("#$index")
if (firstOccurence) {
append(renderMessage(" (type parameter of ${renderFqName(typeParameter.containingDeclaration.fqNameUnsafe)})"))
}
}
}
private val ADAPTIVE_CLASSIFIER_POLICY_KEY = object : RenderingContext.Key<ClassifierNamePolicy>("ADAPTIVE_CLASSIFIER_POLICY") {
override fun compute(objectsToRender: Collection<Any?>): ClassifierNamePolicy {
val ambiguousNames = collectClassifiers(objectsToRender).groupBy { it.name }.filter { it.value.size > 1 }.map { it.key }
return AdaptiveClassifierNamePolicy(ambiguousNames)
}
}
private fun collectClassifiers(objectsToRender: Collection<Any?>): Set<ClassifierDescriptor> = LinkedHashSet<ClassifierDescriptor>().apply {
collectMentionedClassifiers(objectsToRender, this)
}
private fun collectMentionedClassifiers(contextObjects: Collection<Any?>, result: MutableSet<ClassifierDescriptor>) {
contextObjects.filterIsInstance<KotlinType>().forEach { diagnosticType ->
diagnosticType.contains {
innerType ->
innerType.constructor.declarationDescriptor?.let { result.add(it) }
false
}
}
contextObjects.filterIsInstance<Collection<*>>().forEach {
collectMentionedClassifiers(it, result)
}
contextObjects.filterIsInstance<ClassifierDescriptor>().forEach {
result.add(it)
}
contextObjects.filterIsInstance<TypeParameterDescriptor>().forEach {
collectMentionedClassifiers(it.upperBounds, result)
}
contextObjects.filterIsInstance<CallableDescriptor>().forEach {
collectMentionedClassifiers(listOf(
it.typeParameters,
it.returnType,
it.valueParameters,
it.dispatchReceiverParameter?.type,
it.extensionReceiverParameter?.type
), result)
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.types.KotlinType
class SmartTypeRenderer(private val baseRenderer: DescriptorRenderer) : DiagnosticParameterRenderer<KotlinType> {
override fun render(obj: KotlinType, renderingContext: RenderingContext): String {
val adaptiveRenderer = baseRenderer.withOptions {
classifierNamePolicy = renderingContext.adaptiveClassifierPolicy
}
return adaptiveRenderer.renderType(obj)
}
}
class SmartDescriptorRenderer(private val baseRenderer: DescriptorRenderer) : DiagnosticParameterRenderer<DeclarationDescriptor> {
override fun render(obj: DeclarationDescriptor, renderingContext: RenderingContext): String {
val adaptiveRenderer = baseRenderer.withOptions {
classifierNamePolicy = renderingContext.adaptiveClassifierPolicy
}
return adaptiveRenderer.render(obj)
}
}