From ced5a6c917416088d9def359edacf548509677f3 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Tue, 16 Feb 2016 20:41:38 +0300 Subject: [PATCH] Introduce RenderingContext and add as parameter to DiagnosticParameterRenderer#render RenderingContext holds data about the whole diagnostics allowing to adjust rendering of its parameters --- .../diagnostics/DefaultErrorMessagesJvm.java | 7 +- .../kotlin/checkers/CheckerTestUtil.java | 2 +- .../rendering/DefaultErrorMessages.java | 37 ++++++---- .../rendering/DiagnosticParameterRenderer.kt | 8 +- .../rendering/DiagnosticRendererUtil.kt | 5 +- .../kotlin/diagnostics/rendering/Renderers.kt | 74 +++++++++---------- .../diagnostics/rendering/RenderingContext.kt | 68 +++++++++++++++++ .../rendering/TabledDescriptorRenderer.java | 42 +++++++++-- .../diagnosticsWithParameterRenderers.kt | 17 +++-- .../resolve/DelegatedPropertyResolver.java | 16 +++- .../AbstractConstraintSystemTest.kt | 4 +- .../HtmlTabledDescriptorRenderer.java | 17 +++-- .../idea/highlighter/IdeErrorMessages.java | 12 +-- .../kotlin/idea/highlighter/IdeRenderers.kt | 24 +++--- .../js/resolve/diagnostics/jsRenderers.kt | 7 +- 15 files changed, 228 insertions(+), 112 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/RenderingContext.kt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 8b059e3b5a9..50cd881fa59 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -18,10 +18,7 @@ package org.jetbrains.kotlin.resolve.jvm.diagnostics; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; -import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap; -import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticParameterRenderer; -import org.jetbrains.kotlin.diagnostics.rendering.Renderers; +import org.jetbrains.kotlin.diagnostics.rendering.*; import org.jetbrains.kotlin.renderer.DescriptorRenderer; import java.util.ArrayList; @@ -33,7 +30,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { private static final DiagnosticParameterRenderer CONFLICTING_JVM_DECLARATIONS_DATA = new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull ConflictingJvmDeclarationsData data) { + public String render(@NotNull ConflictingJvmDeclarationsData data, @NotNull RenderingContext context) { List renderedDescriptors = new ArrayList(); for (JvmDeclarationOrigin origin : data.getSignatureOrigins()) { DeclarationDescriptor descriptor = origin.getDescriptor(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java index 19d39f1d56d..81df046e67e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/checkers/CheckerTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 32f27ead4af..6482a9375f5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -41,6 +41,7 @@ import java.util.List; import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.diagnostics.rendering.Renderers.*; +import static org.jetbrains.kotlin.diagnostics.rendering.RenderingContext.*; public class DefaultErrorMessages { @@ -118,11 +119,13 @@ public class DefaultErrorMessages { @NotNull @Override public String[] render(@NotNull TypeMismatchDueToTypeProjectionsData object) { + RenderingContext context = + of(object.getExpectedType(), object.getExpressionType(), object.getReceiverType(), object.getCallableDescriptor()); return new String[] { - RENDER_TYPE.render(object.getExpectedType()), - RENDER_TYPE.render(object.getExpressionType()), - RENDER_TYPE.render(object.getReceiverType()), - DescriptorRenderer.FQ_NAMES_IN_TYPES.render(object.getCallableDescriptor()) + RENDER_TYPE.render(object.getExpectedType(), context), + RENDER_TYPE.render(object.getExpressionType(), context), + RENDER_TYPE.render(object.getReceiverType(), context), + FQ_NAMES_IN_TYPES.render(object.getCallableDescriptor(), context) }; } }); @@ -175,7 +178,7 @@ public class DefaultErrorMessages { MAP.put(NAMED_ARGUMENTS_NOT_ALLOWED, "Named arguments are not allowed for {0}", new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull BadNamedArgumentsTarget target) { + public String render(@NotNull BadNamedArgumentsTarget target, @NotNull RenderingContext context) { switch (target) { case NON_KOTLIN_FUNCTION: return "non-Kotlin functions"; @@ -391,7 +394,7 @@ public class DefaultErrorMessages { MAP.put(EXPRESSION_EXPECTED, "{0} is not an expression, and only expressions are allowed here", new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull KtExpression expression) { + public String render(@NotNull KtExpression expression, @NotNull RenderingContext context) { String expressionType = expression.toString(); return expressionType.substring(0, 1) + expressionType.substring(1).toLowerCase(); @@ -487,7 +490,7 @@ public class DefaultErrorMessages { MAP.put(NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER, "{0} does not refer to a type parameter of {1}", new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull KtTypeConstraint typeConstraint) { + public String render(@NotNull KtTypeConstraint typeConstraint, @NotNull RenderingContext context) { //noinspection ConstantConditions return typeConstraint.getSubjectTypeParameterName().getReferencedName(); } @@ -509,11 +512,13 @@ public class DefaultErrorMessages { @NotNull @Override public String[] render(@NotNull VarianceConflictDiagnosticData data) { + RenderingContext context = + of(data.getTypeParameter(), data.getTypeParameter().getVariance(), data.getOccurrencePosition(), data.getContainingType()); return new String[] { - NAME.render(data.getTypeParameter()), - RENDER_POSITION_VARIANCE.render(data.getTypeParameter().getVariance()), - RENDER_POSITION_VARIANCE.render(data.getOccurrencePosition()), - RENDER_TYPE.render(data.getContainingType()) + NAME.render(data.getTypeParameter(), context), + RENDER_POSITION_VARIANCE.render(data.getTypeParameter().getVariance(), context), + RENDER_POSITION_VARIANCE.render(data.getOccurrencePosition(), context), + RENDER_TYPE.render(data.getContainingType(), context) }; } }); @@ -548,7 +553,7 @@ public class DefaultErrorMessages { MAP.put(EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull KtSimpleNameExpression nameExpression) { + public String render(@NotNull KtSimpleNameExpression nameExpression, @NotNull RenderingContext context) { //noinspection ConstantConditions return nameExpression.getReferencedName(); } @@ -603,15 +608,15 @@ public class DefaultErrorMessages { ELEMENT_TEXT, new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull KotlinType type) { + public String render(@NotNull KotlinType type, @NotNull RenderingContext context) { if (type.isError()) return ""; - return " of type '" + RENDER_TYPE.render(type) + "'"; + return " of type '" + RENDER_TYPE.render(type, context) + "'"; } }); MAP.put(FUNCTION_CALL_EXPECTED, "Function invocation ''{0}({1})'' expected", ELEMENT_TEXT, new DiagnosticParameterRenderer() { @NotNull @Override - public String render(@NotNull Boolean hasValueParameters) { + public String render(@NotNull Boolean hasValueParameters, @NotNull RenderingContext context) { return hasValueParameters ? "..." : ""; } }); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticParameterRenderer.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticParameterRenderer.kt index 5efb23fcc28..1d445f643ca 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticParameterRenderer.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticParameterRenderer.kt @@ -17,9 +17,13 @@ package org.jetbrains.kotlin.diagnostics.rendering interface DiagnosticParameterRenderer { - fun render(obj: O): String + fun render(obj: O, renderingContext: RenderingContext): String } fun Renderer(block: (O) -> String) = object : DiagnosticParameterRenderer { - override fun render(obj: O) = block(obj) + override fun render(obj: O, renderingContext: RenderingContext): String = block(obj) +} + +fun ContextDependentRenderer(block: (O, RenderingContext) -> String) = object : DiagnosticParameterRenderer { + override fun render(obj: O, renderingContext: RenderingContext): String = block(obj, renderingContext) } \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticRendererUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticRendererUtil.kt index 644df659948..34a20f8d8eb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticRendererUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DiagnosticRendererUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -19,7 +19,8 @@ package org.jetbrains.kotlin.diagnostics.rendering import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.renderer.DescriptorRenderer -fun

renderParameter(parameter: P, renderer: DiagnosticParameterRenderer

?): Any = renderer?.render(parameter) ?: parameter +fun

renderParameter(parameter: P, renderer: DiagnosticParameterRenderer

?, context: RenderingContext): Any + = renderer?.render(parameter, context) ?: parameter fun ClassDescriptor.renderKindWithName(): String = DescriptorRenderer.getClassKindPrefix(this) + " '" + name + "'" diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt index 1703a62e537..8bfd10ad4e0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/Renderers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -122,19 +122,20 @@ object Renderers { @JvmField val AMBIGUOUS_CALLS = Renderer { calls: Collection> -> - calls - .map { it.resultingDescriptor } + val descriptors = calls.map { it.resultingDescriptor } + val context = RenderingContext.Impl(descriptors) + descriptors .sortedWith(MemberComparator.INSTANCE) - .joinToString(separator = "\n", prefix = "\n") { DescriptorRenderer.FQ_NAMES_IN_TYPES.render(it) } + .joinToString(separator = "\n", prefix = "\n") { FQ_NAMES_IN_TYPES.render(it, context) } } - @JvmStatic fun commaSeparated(itemRenderer: DiagnosticParameterRenderer) = Renderer> { - collection -> + @JvmStatic fun commaSeparated(itemRenderer: DiagnosticParameterRenderer) = ContextDependentRenderer> { + collection, context -> buildString { val iterator = collection.iterator() while (iterator.hasNext()) { val next = iterator.next() - append(itemRenderer.render(next)) + append(itemRenderer.render(next, context)) if (iterator.hasNext()) { append(", ") } @@ -166,7 +167,7 @@ object Renderers { inferenceErrorData: InferenceErrorData, result: TabledDescriptorRenderer ): TabledDescriptorRenderer { LOG.assertTrue(inferenceErrorData.constraintSystem.status.hasConflictingConstraints(), - renderDebugMessage("Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData)) + debugMessage("Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData)) val substitutedDescriptors = Lists.newArrayList() val substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(inferenceErrorData.constraintSystem) @@ -177,7 +178,7 @@ object Renderers { val firstConflictingVariable = ConstraintsUtil.getFirstConflictingVariable(inferenceErrorData.constraintSystem) if (firstConflictingVariable == null) { - LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData)) + LOG.error(debugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData)) return result } @@ -238,7 +239,7 @@ object Renderers { val firstUnknownVariable = inferenceErrorData.constraintSystem.typeVariables.firstOrNull { variable -> inferenceErrorData.constraintSystem.getTypeBounds(variable).values.isEmpty() } ?: return result.apply { - LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData)) + LOG.error(debugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData)) } return result @@ -256,7 +257,7 @@ object Renderers { val constraintSystem = inferenceErrorData.constraintSystem val status = constraintSystem.status LOG.assertTrue(status.hasViolatedUpperBound(), - renderDebugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)) + debugMessage("Upper bound violated renderer is applied for incorrect status", inferenceErrorData)) val systemWithoutWeakConstraints = constraintSystem.filterConstraintsOut(ConstraintPositionKind.TYPE_BOUND_POSITION) val typeParameterDescriptor = inferenceErrorData.descriptor.typeParameters.firstOrNull { @@ -266,15 +267,15 @@ object Renderers { return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result) } if (typeParameterDescriptor == null) { - LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error", inferenceErrorData)) + LOG.error(debugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error", inferenceErrorData)) return result } val typeVariable = systemWithoutWeakConstraints.descriptorToVariable(inferenceErrorData.call.toHandle(), typeParameterDescriptor) val inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeVariable).value if (inferredValueForTypeParameter == null) { - LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " + - typeParameterDescriptor.name + "\n: " + systemWithoutWeakConstraints, inferenceErrorData)) + LOG.error(debugMessage("System without weak constraints is not successful, there is no value for type parameter " + + typeParameterDescriptor.name + "\n: " + systemWithoutWeakConstraints, inferenceErrorData)) return result } @@ -295,17 +296,19 @@ object Renderers { } } if (violatedUpperBound == null) { - LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + - typeParameterDescriptor.name + " violates no bounds after substitution", inferenceErrorData)) + 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)) + .error(typeRenderer.render(inferredValueForTypeParameter, context)) .normal(" is not a subtype of ") - .strong(typeRenderer.render(violatedUpperBound))) + .strong(typeRenderer.render(violatedUpperBound, context))) return result } @@ -316,7 +319,7 @@ object Renderers { val errors = system.status.constraintErrors val typeVariableWithCapturedConstraint = errors.firstIsInstanceOrNull()?.typeVariable if (typeVariableWithCapturedConstraint == null) { - LOG.error(renderDebugMessage("An error 'cannot capture type parameter' is not found in errors", inferenceErrorData)) + LOG.error(debugMessage("An error 'cannot capture type parameter' is not found in errors", inferenceErrorData)) return result } @@ -324,7 +327,7 @@ object Renderers { val boundWithCapturedType = typeBounds.bounds.firstOrNull { it.constrainingType.isCaptured() } val capturedTypeConstructor = boundWithCapturedType?.constrainingType?.constructor as? CapturedTypeConstructor if (capturedTypeConstructor == null) { - LOG.error(renderDebugMessage("There is no captured type in bounds, but there is an error 'cannot capture type parameter'", inferenceErrorData)) + LOG.error(debugMessage("There is no captured type in bounds, but there is an error 'cannot capture type parameter'", inferenceErrorData)) return result } @@ -333,7 +336,7 @@ object Renderers { val explanation: String val upperBound = TypeIntersector.getUpperBoundsAsType(typeParameter) if (!KotlinBuiltIns.isNullableAny(upperBound) && capturedTypeConstructor.typeProjection.projectionKind == Variance.IN_VARIANCE) { - explanation = "Type parameter has an upper bound '" + result.typeRenderer.render(upperBound) + "'" + + explanation = "Type parameter has an upper bound '" + result.typeRenderer.render(upperBound, RenderingContext.of(upperBound)) + "'" + " that cannot be satisfied capturing 'in' projection" } else { @@ -365,24 +368,20 @@ object Renderers { } } - private fun renderTypes(types: Collection) = StringUtil.join(types, { RENDER_TYPE.render(it) }, ", ") + private fun renderTypes(types: Collection, context: RenderingContext) = StringUtil.join(types, { RENDER_TYPE.render(it, context) }, ", ") - @JvmField val RENDER_COLLECTION_OF_TYPES = Renderer> { renderTypes(it) } + @JvmField val RENDER_COLLECTION_OF_TYPES = ContextDependentRenderer> { types, context -> renderTypes(types, context) } - private fun renderConstraintSystem(constraintSystem: ConstraintSystem, renderTypeBounds: DiagnosticParameterRenderer): String { + fun renderConstraintSystem(constraintSystem: ConstraintSystem, shortTypeBounds: Boolean): String { val typeBounds = linkedSetOf() for (variable in constraintSystem.typeVariables) { typeBounds.add(constraintSystem.getTypeBounds(variable)) } return "type parameter bounds:\n" + - StringUtil.join(typeBounds, { renderTypeBounds.render(it) }, "\n") + "\n\n" + "status:\n" + + StringUtil.join(typeBounds, { renderTypeBounds(it, short = shortTypeBounds) }, "\n") + "\n\n" + "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.status) } - @JvmField val RENDER_CONSTRAINT_SYSTEM = Renderer { renderConstraintSystem(it, RENDER_TYPE_BOUNDS) } - - @JvmField val RENDER_CONSTRAINT_SYSTEM_SHORT = Renderer { renderConstraintSystem(it, RENDER_TYPE_BOUNDS_SHORT) } - private fun renderTypeBounds(typeBounds: TypeBounds, short: Boolean): String { val renderBound = { bound: Bound -> val arrow = if (bound.kind == LOWER_BOUND) ">: " else if (bound.kind == UPPER_BOUND) "<: " else ":= " @@ -398,28 +397,25 @@ object Renderers { "$typeVariableName ${StringUtil.join(typeBounds.bounds, renderBound, ", ")}" } - @JvmField val RENDER_TYPE_BOUNDS = Renderer { renderTypeBounds(it, short = false) } - - @JvmField val RENDER_TYPE_BOUNDS_SHORT = Renderer { renderTypeBounds(it, short = true) } - - private fun renderDebugMessage(message: String, inferenceErrorData: InferenceErrorData) = buildString { + private fun debugMessage(message: String, inferenceErrorData: InferenceErrorData) = buildString { append(message) append("\nConstraint system: \n") - append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem)) + append(renderConstraintSystem(inferenceErrorData.constraintSystem, false)) 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.render(inferenceErrorData.expectedType)) + append(RENDER_TYPE.render(inferenceErrorData.expectedType, context)) } append("\nArgument types:\n") if (inferenceErrorData.receiverArgumentType != null) { - append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".") + append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType, context)).append(".") } - append("(").append(renderTypes(inferenceErrorData.valueArgumentsTypes)).append(")") + append("(").append(renderTypes(inferenceErrorData.valueArgumentsTypes, context)).append(")") } private val WHEN_MISSING_LIMIT = 7 @@ -448,4 +444,4 @@ object Renderers { fun DescriptorRenderer.asRenderer() = Renderer { render(it) -} \ No newline at end of file +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/RenderingContext.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/RenderingContext.kt new file mode 100644 index 00000000000..0c2c56b5e25 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/RenderingContext.kt @@ -0,0 +1,68 @@ +/* + * 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.diagnostics.* + +// holds data about the parameters of the diagnostic we're about to render +sealed class RenderingContext { + abstract operator fun get(key: Key): T + + abstract class Key(val name: String) { + abstract fun compute(objectsToRender: Collection): T + } + + class Impl(private val objectsToRender: Collection) : RenderingContext() { + private val data = linkedMapOf, Any?>() + + override fun get(key: Key): T { + if (!data.containsKey(key)) { + val result = key.compute(objectsToRender) + data[key] = result + return result + } + return data[key] as T + } + } + + + object Empty : RenderingContext() { + override fun get(key: Key): T { + return key.compute(emptyList()) + } + } + + companion object { + @JvmStatic + fun of(vararg objectsToRender: Any?): RenderingContext { + return Impl(objectsToRender.toList()) + } + + @JvmStatic + fun fromDiagnostic(d: Diagnostic): RenderingContext { + val parameters = when (d) { + is SimpleDiagnostic<*> -> listOf() + is DiagnosticWithParameters1<*, *> -> listOf(d.a) + is DiagnosticWithParameters2<*, *, *> -> listOf(d.a, d.b) + is DiagnosticWithParameters3<*, *, *, *> -> listOf(d.a, d.b, d.c) + is ParametrizedDiagnostic<*> -> error("Unexpected diagnostic: ${d.javaClass}") + else -> listOf() + } + return Impl(parameters) + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/TabledDescriptorRenderer.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/TabledDescriptorRenderer.java index f9677583991..05e5c1eb896 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/TabledDescriptorRenderer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/TabledDescriptorRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -26,10 +26,10 @@ import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.Table import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.FunctionArgumentsRow; import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.TableRow; import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TextRenderer.TextElement; -import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition; import org.jetbrains.kotlin.types.KotlinType; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -167,26 +167,33 @@ public class TabledDescriptorRenderer { protected void renderTable(TableRenderer table, StringBuilder result) { if (table.rows.isEmpty()) return; + + RenderingContext context = computeRenderingContext(table); for (TableRow row : table.rows) { if (row instanceof TextRenderer) { renderText((TextRenderer) row, result); } if (row instanceof DescriptorRow) { - result.append(DescriptorRenderer.COMPACT.render(((DescriptorRow) row).descriptor)); + result.append(Renderers.COMPACT.render(((DescriptorRow) row).descriptor, context)); } if (row instanceof FunctionArgumentsRow) { FunctionArgumentsRow functionArgumentsRow = (FunctionArgumentsRow) row; - renderFunctionArguments(functionArgumentsRow.receiverType, functionArgumentsRow.argumentTypes, result); + renderFunctionArguments(functionArgumentsRow.receiverType, functionArgumentsRow.argumentTypes, result, context); } result.append("\n"); } } - private void renderFunctionArguments(@Nullable KotlinType receiverType, @NotNull List argumentTypes, StringBuilder result) { + private void renderFunctionArguments( + @Nullable KotlinType receiverType, + @NotNull List argumentTypes, + StringBuilder result, + @NotNull RenderingContext context + ) { boolean hasReceiver = receiverType != null; if (hasReceiver) { result.append("receiver: "); - result.append(getTypeRenderer().render(receiverType)); + result.append(getTypeRenderer().render(receiverType, context)); result.append(" arguments: "); } if (argumentTypes.isEmpty()) { @@ -197,7 +204,7 @@ public class TabledDescriptorRenderer { result.append("("); for (Iterator iterator = argumentTypes.iterator(); iterator.hasNext(); ) { KotlinType argumentType = iterator.next(); - String renderedArgument = getTypeRenderer().render(argumentType); + String renderedArgument = getTypeRenderer().render(argumentType, context); result.append(renderedArgument); if (iterator.hasNext()) { @@ -212,4 +219,25 @@ public class TabledDescriptorRenderer { } public static enum TextElementType { STRONG, ERROR, DEFAULT } + + @NotNull + protected static RenderingContext computeRenderingContext(@NotNull TableRenderer table) { + ArrayList toRender = new ArrayList(); + for (TableRow row : table.rows) { + if (row instanceof DescriptorRow) { + toRender.add(((DescriptorRow) row).descriptor); + } + else if (row instanceof FunctionArgumentsRow) { + toRender.add(((FunctionArgumentsRow) row).receiverType); + toRender.addAll(((FunctionArgumentsRow) row).argumentTypes); + } + else if (row instanceof TextRenderer) { + + } + else { + throw new AssertionError("Unknown row of type " + row.getClass()); + } + } + return new RenderingContext.Impl(toRender); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/diagnosticsWithParameterRenderers.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/diagnosticsWithParameterRenderers.kt index 42b567ed62c..7aeccb37281 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/diagnosticsWithParameterRenderers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/diagnosticsWithParameterRenderers.kt @@ -42,10 +42,9 @@ class DiagnosticWithParameters1Renderer( ) : AbstractDiagnosticWithParametersRenderer>(message) { override fun renderParameters(diagnostic: DiagnosticWithParameters1<*, A>): Array { - return arrayOf(renderParameter(diagnostic.a, rendererForA)) + val context = RenderingContext.of(diagnostic.a) + return arrayOf(renderParameter(diagnostic.a, rendererForA, context)) } - - } class DiagnosticWithParameters2Renderer( @@ -55,9 +54,10 @@ class DiagnosticWithParameters2Renderer( ) : AbstractDiagnosticWithParametersRenderer>(message) { override fun renderParameters(diagnostic: DiagnosticWithParameters2<*, A, B>): Array { + val context = RenderingContext.of(diagnostic.a, diagnostic.b) return arrayOf( - renderParameter(diagnostic.a, rendererForA), - renderParameter(diagnostic.b, rendererForB) + renderParameter(diagnostic.a, rendererForA, context), + renderParameter(diagnostic.b, rendererForB, context) ) } } @@ -70,10 +70,11 @@ class DiagnosticWithParameters3Renderer( ) : AbstractDiagnosticWithParametersRenderer>(message) { override fun renderParameters(diagnostic: DiagnosticWithParameters3<*, A, B, C>): Array { + val context = RenderingContext.of(diagnostic.a, diagnostic.b, diagnostic.c) return arrayOf( - renderParameter(diagnostic.a, rendererForA), - renderParameter(diagnostic.b, rendererForB), - renderParameter(diagnostic.c, rendererForC) + renderParameter(diagnostic.a, rendererForA, context), + renderParameter(diagnostic.b, rendererForB, context), + renderParameter(diagnostic.c, rendererForC, context) ) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java index b297cd15d34..5e8c2620c99 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -17,15 +17,17 @@ package org.jetbrains.kotlin.resolve; import com.google.common.collect.Lists; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.util.Function; import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; -import org.jetbrains.kotlin.diagnostics.rendering.Renderers; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem; import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemCompleter; @@ -34,8 +36,8 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory; -import org.jetbrains.kotlin.resolve.scopes.ScopeUtils; import org.jetbrains.kotlin.resolve.scopes.LexicalScope; +import org.jetbrains.kotlin.resolve.scopes.ScopeUtils; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.validation.OperatorValidator; import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator; @@ -308,7 +310,13 @@ public class DelegatedPropertyResolver { argumentTypes.add(context.getType(argument.getArgumentExpression())); } - builder.append(Renderers.RENDER_COLLECTION_OF_TYPES.render(argumentTypes)); + String arguments = StringUtil.join(argumentTypes, new Function() { + @Override + public String fun(KotlinType type) { + return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type); + } + }, ", "); + builder.append(arguments); builder.append(")"); return builder.toString(); } diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index 53f7dcdac83..245a6245b83 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -110,7 +110,7 @@ abstract class AbstractConstraintSystemTest() : KotlinLiteFixture() { val system = builder.build() - val resultingStatus = Renderers.RENDER_CONSTRAINT_SYSTEM_SHORT.render(system) + val resultingStatus = Renderers.renderConstraintSystem(system, shortTypeBounds = true) val resultingSubstitutor = system.resultingSubstitutor val result = typeParameterDescriptors.map { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/HtmlTabledDescriptorRenderer.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/HtmlTabledDescriptorRenderer.java index 1681670d7e1..737fb172390 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/HtmlTabledDescriptorRenderer.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/HtmlTabledDescriptorRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticParameterRenderer; +import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext; import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer; import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.DescriptorRow; import org.jetbrains.kotlin.diagnostics.rendering.TabledDescriptorRenderer.TableRenderer.FunctionArgumentsRow; @@ -94,9 +95,10 @@ public class HtmlTabledDescriptorRenderer extends TabledDescriptorRenderer { @Override protected void renderTable(TableRenderer table, StringBuilder result) { if (table.rows.isEmpty()) return; + + RenderingContext context = computeRenderingContext(table); + int rowsNumber = countColumnNumber(table); - - result.append(""); for (TableRow row : table.rows) { result.append(""); @@ -111,7 +113,7 @@ public class HtmlTabledDescriptorRenderer extends TabledDescriptorRenderer { } if (row instanceof FunctionArgumentsRow) { FunctionArgumentsRow functionArgumentsRow = (FunctionArgumentsRow) row; - renderFunctionArguments(functionArgumentsRow.receiverType, functionArgumentsRow.argumentTypes, functionArgumentsRow.isErrorPosition, result); + renderFunctionArguments(functionArgumentsRow.receiverType, functionArgumentsRow.argumentTypes, functionArgumentsRow.isErrorPosition, result, context); } result.append(""); } @@ -124,7 +126,8 @@ public class HtmlTabledDescriptorRenderer extends TabledDescriptorRenderer { @Nullable KotlinType receiverType, @NotNull List argumentTypes, Predicate isErrorPosition, - StringBuilder result + StringBuilder result, + @NotNull RenderingContext context ) { boolean hasReceiver = receiverType != null; tdSpace(result); @@ -134,7 +137,7 @@ public class HtmlTabledDescriptorRenderer extends TabledDescriptorRenderer { if (isErrorPosition.apply(RECEIVER_POSITION.position())) { error = true; } - receiver = "receiver: " + RenderersUtilKt.renderStrong(getTypeRenderer().render(receiverType), error); + receiver = "receiver: " + RenderersUtilKt.renderStrong(getTypeRenderer().render(receiverType, context), error); } td(result, receiver); td(result, hasReceiver ? "arguments: " : ""); @@ -151,7 +154,7 @@ public class HtmlTabledDescriptorRenderer extends TabledDescriptorRenderer { if (isErrorPosition.apply(VALUE_PARAMETER_POSITION.position(i))) { error = true; } - String renderedArgument = getTypeRenderer().render(argumentType); + String renderedArgument = getTypeRenderer().render(argumentType, context); tdRight(result, RenderersUtilKt.renderStrong(renderedArgument, error) + (iterator.hasNext() ? RenderersUtilKt.renderStrong(",") : "")); i++; diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java index 941e062eba6..dc856c556b7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -71,11 +71,13 @@ public class IdeErrorMessages { @NotNull @Override public String[] render(@NotNull TypeMismatchDueToTypeProjectionsData object) { + RenderingContext context = RenderingContext + .of(object.getExpectedType(), object.getExpressionType(), object.getReceiverType(), object.getCallableDescriptor()); return new String[] { - HTML_RENDER_TYPE.render(object.getExpectedType()), - HTML_RENDER_TYPE.render(object.getExpressionType()), - HTML_RENDER_TYPE.render(object.getReceiverType()), - HTML.render(object.getCallableDescriptor()) + HTML_RENDER_TYPE.render(object.getExpectedType(), context), + HTML_RENDER_TYPE.render(object.getExpressionType(), context), + HTML_RENDER_TYPE.render(object.getReceiverType(), context), + HTML.render(object.getCallableDescriptor(), context) }; } }); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeRenderers.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeRenderers.kt index b21f0747335..38d14e74907 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeRenderers.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeRenderers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.highlighter import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.diagnostics.rendering.ContextDependentRenderer import org.jetbrains.kotlin.diagnostics.rendering.Renderer import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.diagnostics.rendering.asRenderer @@ -67,28 +68,29 @@ object IdeRenderers { Renderers.renderUpperBoundViolatedInferenceError(it, HtmlTabledDescriptorRenderer.create()).toString() } - @JvmField val HTML_RENDER_RETURN_TYPE = Renderer { - val returnType = it.returnType!! - DescriptorRenderer.HTML.renderType(returnType) + @JvmField val HTML_RENDER_RETURN_TYPE = ContextDependentRenderer { + member, context -> + HTML_RENDER_TYPE.render(member.returnType!!, context) } @JvmField val HTML_COMPACT_WITH_MODIFIERS = DescriptorRenderer.HTML.withOptions { withDefinedIn = false }.asRenderer() - @JvmField val HTML_CONFLICTING_JVM_DECLARATIONS_DATA = Renderer { - data: ConflictingJvmDeclarationsData -> + @JvmField val HTML_CONFLICTING_JVM_DECLARATIONS_DATA = ContextDependentRenderer { + data: ConflictingJvmDeclarationsData, renderingContext -> val conflicts = data.signatureOrigins - .mapNotNull { it.descriptor } - .sortedWith(MemberComparator.INSTANCE) - .joinToString("") { "
  • " + HTML_COMPACT_WITH_MODIFIERS.render(it) + "
  • \n" } + .mapNotNull { it.descriptor } + .sortedWith(MemberComparator.INSTANCE) + .joinToString("") { "
  • " + HTML_COMPACT_WITH_MODIFIERS.render(it, renderingContext) + "
  • \n" } "The following declarations have the same JVM signature (${data.signature.name}${data.signature.desc}):
    \n
      \n$conflicts
    " } - @JvmField val HTML_THROWABLE = Renderer { - Renderers.THROWABLE.render(it).replace("\n", "
    ") + @JvmField val HTML_THROWABLE = ContextDependentRenderer { + throwable, context -> + Renderers.THROWABLE.render(throwable, context).replace("\n", "
    ") } @JvmField val HTML = DescriptorRenderer.HTML.asRenderer() diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt index 66a5984a21b..edf40981acc 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * 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. @@ -19,9 +19,10 @@ package org.jetbrains.kotlin.js.resolve.diagnostics import com.google.gwt.dev.js.rhino.Utils.isEndOfLine import com.intellij.psi.PsiElement import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticParameterRenderer +import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext object RenderFirstLineOfElementText : DiagnosticParameterRenderer { - override fun render(element: PsiElement): String { + override fun render(element: PsiElement, context: RenderingContext): String { val text = element.text val index = text.indexOf('\n') return if (index == -1) text else text.substring(0, index) + "..." @@ -31,7 +32,7 @@ object RenderFirstLineOfElementText : DiagnosticParameterRenderer { abstract class JsCallDataRenderer : DiagnosticParameterRenderer { protected abstract fun format(data: JsCallDataWithCode): String - override fun render(data: JsCallData): String = + override fun render(data: JsCallData, context: RenderingContext): String = when (data) { is JsCallDataWithCode -> format(data) is JsCallData -> data.message