From ee5ec0b50097f31d0adfe46d0dc27ac3cfce9365 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Sun, 8 Jul 2012 15:35:13 +0400 Subject: [PATCH] added ide renderers for type inference errors --- .../jet/resolve/DescriptorRenderer.java | 98 +++++++++---- .../plugin/highlighter/IdeErrorMessages.java | 5 + .../jet/plugin/highlighter/IdeRenderers.java | 138 +++++++++++++++++- 3 files changed, 205 insertions(+), 36 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index 9e11fb9f07e..570df5b1f0b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -55,30 +55,13 @@ public class DescriptorRenderer implements Renderer { } }; - public static final DescriptorRenderer HTML = new DescriptorRenderer() { - - @Override - protected String escape(String s) { - return s.replaceAll("<", "<"); - } - - @Override - public String renderKeyword(String keyword) { - return "" + keyword + ""; - } - - @Override - public String renderMessage(String s) { - return "" + s + ""; - } - }; - + public static final DescriptorRenderer HTML = new HtmlDescriptorRenderer(); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final RenderDeclarationDescriptorVisitor rootVisitor = new RenderDeclarationDescriptorVisitor(); - private final DeclarationDescriptorVisitor subVisitor = new RenderDeclarationDescriptorVisitor() { + protected final DeclarationDescriptorVisitor subVisitor = new RenderDeclarationDescriptorVisitor() { @Override public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) { renderTypeParameter(descriptor, builder, false); @@ -96,8 +79,17 @@ public class DescriptorRenderer implements Renderer { }; private DescriptorRenderer() { + this(true, true); } + public DescriptorRenderer(boolean renderModifiers, boolean renderDefinedIn) { + this.renderModifiers = renderModifiers; + this.renderDefinedIn = renderDefinedIn; + } + + private final boolean renderModifiers; + private final boolean renderDefinedIn; + protected boolean hasDefaultValue(ValueParameterDescriptor descriptor) { return descriptor.hasDefaultValue(); } @@ -256,7 +248,7 @@ public class DescriptorRenderer implements Renderer { public String renderFunctionParameters(@NotNull FunctionDescriptor functionDescriptor) { StringBuilder stringBuilder = new StringBuilder(); - rootVisitor.renderValueParameters(functionDescriptor, stringBuilder); + renderValueParameters(functionDescriptor, stringBuilder); return stringBuilder.toString(); } @@ -265,6 +257,7 @@ public class DescriptorRenderer implements Renderer { } private void appendDefinedIn(DeclarationDescriptor declarationDescriptor, StringBuilder stringBuilder) { + if (!renderDefinedIn) return; if (declarationDescriptor instanceof ModuleDescriptor) { stringBuilder.append(" is a module"); return; @@ -291,6 +284,32 @@ public class DescriptorRenderer implements Renderer { return s; } + protected void renderValueParameters(FunctionDescriptor descriptor, StringBuilder builder) { + if (descriptor.getValueParameters().isEmpty()) { + renderEmptyValueParameters(builder); + } + for (Iterator iterator = descriptor.getValueParameters().iterator(); iterator.hasNext(); ) { + renderValueParameter(iterator.next(), !iterator.hasNext(), builder); + } + } + + protected void renderEmptyValueParameters(StringBuilder builder) { + builder.append("()"); + } + + protected void renderValueParameter(ValueParameterDescriptor parameterDescriptor, boolean isLast, StringBuilder builder) { + if (parameterDescriptor.getIndex() == 0) { + builder.append("("); + } + parameterDescriptor.accept(subVisitor, builder); + if (!isLast) { + builder.append(", "); + } + else { + builder.append(")"); + } + } + private class RenderDeclarationDescriptorVisitor implements DeclarationDescriptorVisitor { @Override @@ -368,6 +387,7 @@ public class DescriptorRenderer implements Renderer { } private void renderVisibility(Visibility visibility, StringBuilder builder) { + if(!renderModifiers) return; if ("package".equals(visibility.toString())) { builder.append("public/*package*/ "); } else { @@ -376,6 +396,7 @@ public class DescriptorRenderer implements Renderer { } private void renderModality(Modality modality, StringBuilder builder) { + if (!renderModifiers) return; String keyword = ""; switch (modality) { case FINAL: @@ -422,18 +443,6 @@ public class DescriptorRenderer implements Renderer { return visitFunctionDescriptor(descriptor, data); } - private void renderValueParameters(FunctionDescriptor descriptor, StringBuilder builder) { - builder.append("("); - for (Iterator iterator = descriptor.getValueParameters().iterator(); iterator.hasNext(); ) { - ValueParameterDescriptor parameterDescriptor = iterator.next(); - parameterDescriptor.accept(subVisitor, builder); - if (iterator.hasNext()) { - builder.append(", "); - } - } - builder.append(")"); - } - private void renderWhereSuffix(@NotNull CallableMemberDescriptor callable, @NotNull StringBuilder builder) { boolean first = true; for (TypeParameterDescriptor typeParameter : callable.getTypeParameters()) { @@ -618,4 +627,29 @@ public class DescriptorRenderer implements Renderer { } } } + + public static class HtmlDescriptorRenderer extends DescriptorRenderer { + + public HtmlDescriptorRenderer() { + } + + public HtmlDescriptorRenderer(boolean renderModifiers, boolean renderDefinedIn) { + super(renderModifiers, renderDefinedIn); + } + + @Override + protected String escape(String s) { + return s.replaceAll("<", "<").replaceAll(">", ">"); + } + + @Override + public String renderKeyword(String keyword) { + return "" + keyword + ""; + } + + @Override + public String renderMessage(String s) { + return "" + s + ""; + } + } } diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeErrorMessages.java b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeErrorMessages.java index 692e4bf4954..4bab828d301 100644 --- a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeErrorMessages.java +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeErrorMessages.java @@ -44,6 +44,11 @@ public class IdeErrorMessages { MAP.put(ASSIGN_OPERATOR_AMBIGUITY, "Assignment operators ambiguity. All these functions match.
    {0}
", HTML_AMBIGUOUS_CALLS); + MAP.put(TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS, "Type inference failed: {0}", HTML_INFERENCE_ERROR_CONFLICTING_SUBSTITUTIONS); + MAP.put(TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER, "Type inference failed: {0}", HTML_INFERENCE_NO_INFORMATION_FOR_PARAMETER); + MAP.put(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH, "Type inference failed: {0}", HTML_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH); + MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "{0}", HTML_INFERENCE_UPPER_BOUND_VIOLATED); + MAP.put(WRONG_SETTER_PARAMETER_TYPE, "Setter parameter type must be equal to the type of the property." + "" + "
Expected:{0}
Found:{1}
", HTML_RENDER_TYPE, HTML_RENDER_TYPE); diff --git a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java index 9e127157a08..484e7129b64 100644 --- a/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java +++ b/idea/src/org/jetbrains/jet/plugin/highlighter/IdeRenderers.java @@ -16,12 +16,15 @@ package org.jetbrains.jet.plugin.highlighter; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters1; @@ -33,22 +36,38 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl; import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition; +import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData; +import org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeSubstitutor; +import org.jetbrains.jet.lang.types.Variance; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.resolve.DescriptorRenderer; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author svtk */ public class IdeRenderers { private static final String RED_TEMPLATE = "%s"; + private static final String STRONG_TEMPLATE = "%s"; + + public static String strong(Object o) { + return String.format(STRONG_TEMPLATE, o); + } + + public static String error(Object o) { + return String.format(RED_TEMPLATE, o); + } + + public static String strong(Object o, boolean error) { + return String.format(error ? RED_TEMPLATE : STRONG_TEMPLATE, o); + } public static final Renderer>> HTML_AMBIGUOUS_CALLS = new Renderer>>() { @@ -162,4 +181,115 @@ public class IdeRenderers { } } + public static final Renderer HTML_INFERENCE_ERROR_CONFLICTING_SUBSTITUTIONS = + new Renderer() { + @NotNull + @Override + public String render(@NotNull InferenceErrorData inferenceErrorData) { + Collection substitutedDescriptors = Lists.newArrayList(); + Collection substitutors = inferenceErrorData.constraintSystem.getSubstitutors(); + for (TypeSubstitutor substitutor : substitutors) { + CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor); + substitutedDescriptors.add(substitutedDescriptor); + } + + String type = strong(inferenceErrorData.constraintSystem.getFirstConflictingParameter().getName()); + TablesRenderer table = TablesRenderer.create(); + table.text("Cannot infer type parameter %s in", type) + .descriptor(inferenceErrorData.descriptor) + .text("None of the following substitutions"); + + for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) { + JetType receiverType = substitutedDescriptor.getReceiverParameter().exists() ? substitutedDescriptor.getReceiverParameter().getType() : null; + + Collection errorPositions = Sets.newHashSet(); + List valueArgumentTypes = Lists.newArrayList(); + for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) { + valueArgumentTypes.add(valueParameterDescriptor.getType()); + JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex()); + if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) { + errorPositions.add(ConstraintPosition.valueParameterPosition(valueParameterDescriptor.getIndex())); + } + } + + if (receiverType != null && inferenceErrorData.receiverArgumentType != null && + !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) { + errorPositions.add(ConstraintPosition.RECEIVER_POSITION); + } + + table.functionArgumentTypeList(receiverType, valueArgumentTypes, errorPositions); + } + + table.text("can be applied to") + .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes); + return table.toString(); + } + }; + + + public static final Renderer HTML_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH = + new Renderer() { + @NotNull + @Override + public String render(@NotNull InferenceErrorData inferenceErrorData) { + return TablesRenderer.create() + .descriptor(inferenceErrorData.descriptor) + .text("cannot be applied to") + .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes, + inferenceErrorData.constraintSystem.getErrorConstraintPositions()) + .toString(); + } + }; + + public static final Renderer HTML_INFERENCE_NO_INFORMATION_FOR_PARAMETER = + new Renderer() { + @NotNull + @Override + public String render(@NotNull InferenceErrorData inferenceErrorData) { + TypeParameterDescriptor firstUnknownParameter = null; + for (Map.Entry entry : inferenceErrorData.constraintSystem.getTypeBoundsMap().entrySet()) { + if (entry.getValue().isEmpty()) { + firstUnknownParameter = entry.getKey(); + break; + } + } + assert firstUnknownParameter != null; + String firstUnknownParameterName = String.format(STRONG_TEMPLATE, firstUnknownParameter.getName()); + + return TablesRenderer.create() + .text("Not enough information to infer parameter %s in", firstUnknownParameterName) + .descriptor(inferenceErrorData.descriptor) + .text("Please specify it explicitly.") + .toString(); + } + }; + + public static final Renderer HTML_INFERENCE_UPPER_BOUND_VIOLATED = + new Renderer() { + @NotNull + @Override + public String render(@NotNull InferenceErrorData inferenceErrorData) { + + TypeParameterDescriptor typeParameterDescriptor = null; + for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) { + if (!inferenceErrorData.constraintSystem.checkUpperBound(typeParameter)) { + typeParameterDescriptor = typeParameter; + break; + } + } + assert typeParameterDescriptor != null; + + TablesRenderer table = TablesRenderer.create(); + table.text("Type parameter bound for %s in", strong(typeParameterDescriptor.getName())). + descriptor(inferenceErrorData.descriptor); + + JetType type = inferenceErrorData.constraintSystem.getValue(typeParameterDescriptor); + JetType upperBound = typeParameterDescriptor.getUpperBoundsAsType(); + JetType substitute = inferenceErrorData.constraintSystem.getSubstitutor().substitute(upperBound, Variance.INVARIANT); + + table.newElement() + .text("is not satisfied: inferred type %s is not a subtype of %s.", error(type), strong(substitute)); + return table.toString(); + } + }; }