added ide renderers for type inference errors

This commit is contained in:
Svetlana Isakova
2012-07-08 15:35:13 +04:00
parent 685254d469
commit ee5ec0b500
3 changed files with 205 additions and 36 deletions
@@ -55,30 +55,13 @@ public class DescriptorRenderer implements Renderer<DeclarationDescriptor> {
}
};
public static final DescriptorRenderer HTML = new DescriptorRenderer() {
@Override
protected String escape(String s) {
return s.replaceAll("<", "&lt;");
}
@Override
public String renderKeyword(String keyword) {
return "<b>" + keyword + "</b>";
}
@Override
public String renderMessage(String s) {
return "<i>" + s + "</i>";
}
};
public static final DescriptorRenderer HTML = new HtmlDescriptorRenderer();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private final RenderDeclarationDescriptorVisitor rootVisitor = new RenderDeclarationDescriptorVisitor();
private final DeclarationDescriptorVisitor<Void, StringBuilder> subVisitor = new RenderDeclarationDescriptorVisitor() {
protected final DeclarationDescriptorVisitor<Void, StringBuilder> subVisitor = new RenderDeclarationDescriptorVisitor() {
@Override
public Void visitTypeParameterDescriptor(TypeParameterDescriptor descriptor, StringBuilder builder) {
renderTypeParameter(descriptor, builder, false);
@@ -96,8 +79,17 @@ public class DescriptorRenderer implements Renderer<DeclarationDescriptor> {
};
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<DeclarationDescriptor> {
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<DeclarationDescriptor> {
}
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<DeclarationDescriptor> {
return s;
}
protected void renderValueParameters(FunctionDescriptor descriptor, StringBuilder builder) {
if (descriptor.getValueParameters().isEmpty()) {
renderEmptyValueParameters(builder);
}
for (Iterator<ValueParameterDescriptor> 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<Void, StringBuilder> {
@Override
@@ -368,6 +387,7 @@ public class DescriptorRenderer implements Renderer<DeclarationDescriptor> {
}
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<DeclarationDescriptor> {
}
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<DeclarationDescriptor> {
return visitFunctionDescriptor(descriptor, data);
}
private void renderValueParameters(FunctionDescriptor descriptor, StringBuilder builder) {
builder.append("(");
for (Iterator<ValueParameterDescriptor> 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<DeclarationDescriptor> {
}
}
}
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("<", "&lt;").replaceAll(">", "&gt;");
}
@Override
public String renderKeyword(String keyword) {
return "<b>" + keyword + "</b>";
}
@Override
public String renderMessage(String s) {
return "<i>" + s + "</i>";
}
}
}
@@ -44,6 +44,11 @@ public class IdeErrorMessages {
MAP.put(ASSIGN_OPERATOR_AMBIGUITY, "<html>Assignment operators ambiguity. All these functions match.<ul>{0}</ul></table></html>",
HTML_AMBIGUOUS_CALLS);
MAP.put(TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS, "<html>Type inference failed: {0}</html>", HTML_INFERENCE_ERROR_CONFLICTING_SUBSTITUTIONS);
MAP.put(TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER, "<html>Type inference failed: {0}</html>", HTML_INFERENCE_NO_INFORMATION_FOR_PARAMETER);
MAP.put(TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH, "<html>Type inference failed: {0}</html>", HTML_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH);
MAP.put(TYPE_INFERENCE_UPPER_BOUND_VIOLATED, "<html>{0}</html>", HTML_INFERENCE_UPPER_BOUND_VIOLATED);
MAP.put(WRONG_SETTER_PARAMETER_TYPE, "<html>Setter parameter type must be equal to the type of the property." +
"<table><tr><td>Expected:</td><td>{0}</td></tr>" +
"<tr><td>Found:</td><td>{1}</td></tr></table></html>", HTML_RENDER_TYPE, HTML_RENDER_TYPE);
@@ -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 = "<font color=red><b>%s</b></font>";
private static final String STRONG_TEMPLATE = "<b>%s</b>";
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<Collection<? extends ResolvedCall<? extends CallableDescriptor>>> HTML_AMBIGUOUS_CALLS =
new Renderer<Collection<? extends ResolvedCall<? extends CallableDescriptor>>>() {
@@ -162,4 +181,115 @@ public class IdeRenderers {
}
}
public static final Renderer<InferenceErrorData> HTML_INFERENCE_ERROR_CONFLICTING_SUBSTITUTIONS =
new Renderer<InferenceErrorData>() {
@NotNull
@Override
public String render(@NotNull InferenceErrorData inferenceErrorData) {
Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
Collection<TypeSubstitutor> 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<ConstraintPosition> errorPositions = Sets.newHashSet();
List<JetType> 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<InferenceErrorData> HTML_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH =
new Renderer<InferenceErrorData>() {
@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<InferenceErrorData> HTML_INFERENCE_NO_INFORMATION_FOR_PARAMETER =
new Renderer<InferenceErrorData>() {
@NotNull
@Override
public String render(@NotNull InferenceErrorData inferenceErrorData) {
TypeParameterDescriptor firstUnknownParameter = null;
for (Map.Entry<TypeParameterDescriptor, TypeBounds> 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<InferenceErrorData> HTML_INFERENCE_UPPER_BOUND_VIOLATED =
new Renderer<InferenceErrorData>() {
@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();
}
};
}