added ide renderers for type inference errors
This commit is contained in:
@@ -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("<", "<");
|
||||
}
|
||||
|
||||
@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("<", "<").replaceAll(">", ">");
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user