001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.diagnostics.rendering;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.collect.Lists;
021    import com.google.common.collect.Sets;
022    import com.intellij.openapi.diagnostic.Logger;
023    import com.intellij.openapi.util.text.StringUtil;
024    import com.intellij.psi.PsiElement;
025    import com.intellij.util.Function;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.lang.descriptors.*;
029    import org.jetbrains.jet.lang.psi.JetClass;
030    import org.jetbrains.jet.lang.psi.JetClassOrObject;
031    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
032    import org.jetbrains.jet.lang.resolve.calls.inference.*;
033    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.jet.lang.resolve.name.Name;
035    import org.jetbrains.jet.lang.types.JetType;
036    import org.jetbrains.jet.lang.types.TypeSubstitutor;
037    import org.jetbrains.jet.lang.types.TypeUtils;
038    import org.jetbrains.jet.lang.types.Variance;
039    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040    import org.jetbrains.jet.renderer.DescriptorRenderer;
041    import org.jetbrains.jet.renderer.Renderer;
042    
043    import java.util.Collection;
044    import java.util.Iterator;
045    import java.util.List;
046    import java.util.Set;
047    
048    import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
049    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;
050    import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND;
051    
052    public class Renderers {
053        private static final Logger LOG = Logger.getInstance(Renderers.class);
054    
055        public static final Renderer<Object> TO_STRING = new Renderer<Object>() {
056            @NotNull
057            @Override
058            public String render(@NotNull Object element) {
059                return element.toString();
060            }
061    
062            @Override
063            public String toString() {
064                return "TO_STRING";
065            }
066        };
067    
068        public static final Renderer<Object> NAME = new Renderer<Object>() {
069            @NotNull
070            @Override
071            public String render(@NotNull Object element) {
072                if (element instanceof Named) {
073                    return ((Named) element).getName().asString();
074                }
075                return element.toString();
076            }
077        };
078    
079        public static final Renderer<PsiElement> ELEMENT_TEXT = new Renderer<PsiElement>() {
080            @NotNull
081            @Override
082            public String render(@NotNull PsiElement element) {
083                return element.getText();
084            }
085        };
086    
087        public static final Renderer<JetClassOrObject> RENDER_CLASS_OR_OBJECT = new Renderer<JetClassOrObject>() {
088            @NotNull
089            @Override
090            public String render(@NotNull JetClassOrObject classOrObject) {
091                String name = classOrObject.getName() != null ? " '" + classOrObject.getName() + "'" : "";
092                if (classOrObject instanceof JetClass) {
093                    return "Class" + name;
094                }
095                return "Object" + name;
096    
097            }
098        };
099    
100        public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
101            @NotNull
102            @Override
103            public String render(@NotNull JetType type) {
104                return DescriptorRenderer.TEXT.renderType(type);
105            }
106        };
107    
108        public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
109                new Renderer<Collection<? extends ResolvedCall<?>>>() {
110                    @NotNull
111                    @Override
112                    public String render(@NotNull Collection<? extends ResolvedCall<?>> argument) {
113                        StringBuilder stringBuilder = new StringBuilder("\n");
114                        for (ResolvedCall<?> call : argument) {
115                            stringBuilder.append(DescriptorRenderer.TEXT.render(call.getResultingDescriptor())).append("\n");
116                        }
117                        return stringBuilder.toString();
118                    }
119                };
120    
121        public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
122            return new Renderer<Collection<? extends T>>() {
123                @NotNull
124                @Override
125                public String render(@NotNull Collection<? extends T> object) {
126                    StringBuilder result = new StringBuilder();
127                    for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
128                        T next = iterator.next();
129                        result.append(itemRenderer.render(next));
130                        if (iterator.hasNext()) {
131                            result.append(", ");
132                        }
133                    }
134                    return result.toString();
135                }
136            };
137        }
138    
139        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
140                new Renderer<InferenceErrorData>() {
141                    @NotNull
142                    @Override
143                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
144                        return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
145                    }
146                };
147    
148        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
149                new Renderer<InferenceErrorData>() {
150                    @NotNull
151                    @Override
152                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
153                        return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
154                    }
155                };
156    
157        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
158                new Renderer<InferenceErrorData>() {
159                    @NotNull
160                    @Override
161                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
162                        return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
163                    }
164                };
165    
166        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
167                new Renderer<InferenceErrorData>() {
168                    @NotNull
169                    @Override
170                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
171                        return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
172                    }
173                };
174    
175        public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
176                TabledDescriptorRenderer result) {
177            LOG.assertTrue(inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints(), renderDebugMessage(
178                    "Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData));
179    
180            Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
181            Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
182                    inferenceErrorData.constraintSystem);
183            for (TypeSubstitutor substitutor : substitutors) {
184                CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
185                substitutedDescriptors.add(substitutedDescriptor);
186            }
187    
188            TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
189            if (firstConflictingParameter == null) {
190                LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData));
191                return result;
192            }
193    
194            result.text(newText()
195                                .normal("Cannot infer type parameter ")
196                                .strong(firstConflictingParameter.getName())
197                                .normal(" in "));
198            //String type = strong(firstConflictingParameter.getName());
199            TableRenderer table = newTable();
200            result.table(table);
201            table.descriptor(inferenceErrorData.descriptor)
202                 .text("None of the following substitutions");
203    
204            for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
205                JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
206    
207                final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
208                List<JetType> parameterTypes = Lists.newArrayList();
209                for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
210                    parameterTypes.add(valueParameterDescriptor.getType());
211                    if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
212                    JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
213                    if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
214                        errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
215                    }
216                }
217    
218                if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
219                        !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
220                    errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
221                }
222    
223                Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
224                    @Override
225                    public boolean apply(@Nullable ConstraintPosition constraintPosition) {
226                        return errorPositions.contains(constraintPosition);
227                    }
228                };
229                table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
230            }
231    
232            table.text("can be applied to")
233                    .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
234    
235            return result;
236        }
237    
238        public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
239                TabledDescriptorRenderer renderer) {
240            Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
241                @Override
242                public boolean apply(ConstraintPosition constraintPosition) {
243                    return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
244                }
245            };
246            return renderer.table(TabledDescriptorRenderer.newTable()
247                                          .descriptor(inferenceErrorData.descriptor)
248                                          .text("cannot be applied to")
249                                          .functionArgumentTypeList(
250                                                  inferenceErrorData.receiverArgumentType,
251                                                  inferenceErrorData.valueArgumentsTypes,
252                                                  isErrorPosition));
253        }
254    
255        public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
256                TabledDescriptorRenderer result) {
257            TypeParameterDescriptor firstUnknownParameter = null;
258            for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
259                if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
260                    firstUnknownParameter = typeParameter;
261                    break;
262                }
263            }
264            if (firstUnknownParameter == null) {
265                LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData));
266                return result;
267            }
268    
269            return result
270                    .text(newText().normal("Not enough information to infer parameter ")
271                                  .strong(firstUnknownParameter.getName())
272                                  .normal(" in "))
273                    .table(newTable()
274                                   .descriptor(inferenceErrorData.descriptor)
275                                   .text("Please specify it explicitly."));
276        }
277    
278        @NotNull
279        public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
280            TypeParameterDescriptor typeParameterDescriptor = null;
281            ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
282            ConstraintSystemStatus status = constraintSystem.getStatus();
283            LOG.assertTrue(status.hasViolatedUpperBound(), renderDebugMessage(
284                    "Upper bound violated renderer is applied for incorrect status", inferenceErrorData));
285    
286            ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
287            for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
288                if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
289                    typeParameterDescriptor = typeParameter;
290                }
291            }
292            if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
293                return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result);
294            }
295            if (typeParameterDescriptor == null) {
296                LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error",
297                                             inferenceErrorData));
298                return result;
299            }
300    
301            JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
302            if (inferredValueForTypeParameter == null) {
303                LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
304                                             typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData));
305                return result;
306            }
307    
308            result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
309                    .table(newTable().
310                            descriptor(inferenceErrorData.descriptor));
311    
312            JetType violatedUpperBound = null;
313            for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
314                JetType upperBoundWithSubstitutedInferredTypes =
315                        systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
316                if (upperBoundWithSubstitutedInferredTypes != null &&
317                    !JetTypeChecker.INSTANCE.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
318                    violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
319                    break;
320                }
321            }
322            if (violatedUpperBound == null) {
323                LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + typeParameterDescriptor.getName() +
324                                             " violates no bounds after substitution", inferenceErrorData));
325                return result;
326            }
327    
328            Renderer<JetType> typeRenderer = result.getTypeRenderer();
329            result.text(newText()
330                                .normal(" is not satisfied: inferred type ")
331                                .error(typeRenderer.render(inferredValueForTypeParameter))
332                                .normal(" is not a subtype of ")
333                                .strong(typeRenderer.render(violatedUpperBound)));
334            return result;
335        }
336    
337        public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
338            @NotNull
339            @Override
340            public String render(@NotNull Collection<ClassDescriptor> descriptors) {
341                StringBuilder sb = new StringBuilder();
342                int index = 0;
343                for (ClassDescriptor descriptor : descriptors) {
344                    sb.append(DescriptorUtils.getFqName(descriptor).asString());
345                    index++;
346                    if (index <= descriptors.size() - 2) {
347                        sb.append(", ");
348                    }
349                    else if (index == descriptors.size() - 1) {
350                        sb.append(" or ");
351                    }
352                }
353                return sb.toString();
354            }
355        };
356    
357        public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
358            @NotNull
359            @Override
360            public String render(@NotNull Collection<JetType> types) {
361                return StringUtil.join(types, new Function<JetType, String>() {
362                    @Override
363                    public String fun(JetType type) {
364                        return RENDER_TYPE.render(type);
365                    }
366                }, ", ");
367            }
368        };
369    
370        public static final Renderer<ConstraintSystem> RENDER_CONSTRAINT_SYSTEM = new Renderer<ConstraintSystem>() {
371            @NotNull
372            @Override
373            public String render(@NotNull ConstraintSystem constraintSystem) {
374                Set<TypeParameterDescriptor> typeVariables = constraintSystem.getTypeVariables();
375                Set<TypeBounds> typeBounds = Sets.newLinkedHashSet();
376                for (TypeParameterDescriptor variable : typeVariables) {
377                    typeBounds.add(constraintSystem.getTypeBounds(variable));
378                }
379                Function<TypeBounds, String> renderTypeBounds = rendererToFunction(RENDER_TYPE_BOUNDS);
380                return "type parameter bounds:\n" + StringUtil.join(typeBounds, renderTypeBounds, "\n") + "\n" +
381                       "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.getStatus());
382            }
383        };
384    
385        public static final Renderer<TypeBounds> RENDER_TYPE_BOUNDS = new Renderer<TypeBounds>() {
386            @NotNull
387            @Override
388            public String render(@NotNull TypeBounds typeBounds) {
389                Function<TypeBoundsImpl.Bound, String> renderBound = new Function<TypeBoundsImpl.Bound, String>() {
390                    @Override
391                    public String fun(TypeBoundsImpl.Bound bound) {
392                        String arrow = bound.kind == LOWER_BOUND ? ">: " : bound.kind == UPPER_BOUND ? "<: " : ":= ";
393                        return arrow + RENDER_TYPE.render(bound.type) + '(' + bound.position + ')';
394                    }
395                };
396                Name typeVariableName = typeBounds.getTypeVariable().getName();
397                if (typeBounds.isEmpty()) {
398                    return typeVariableName.asString();
399                }
400                return typeVariableName + " " + StringUtil.join(typeBounds.getBounds(), renderBound, ", ");
401            }
402        };
403    
404        @NotNull
405        public static <T> Function<T, String> rendererToFunction(final @NotNull Renderer<T> renderer) {
406            return new Function<T, String>() {
407                @Override
408                public String fun(T t) {
409                    return renderer.render(t);
410                }
411            };
412        }
413    
414        @NotNull
415        private static String renderDebugMessage(String message, InferenceErrorData inferenceErrorData) {
416            StringBuilder result = new StringBuilder();
417            result.append(message);
418            result.append("\nConstraint system: \n");
419            result.append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem));
420            result.append("\nDescriptor:\n");
421            result.append(inferenceErrorData.descriptor);
422            result.append("\nExpected type:\n");
423            if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
424                result.append(inferenceErrorData.expectedType);
425            }
426            else {
427                result.append(RENDER_TYPE.render(inferenceErrorData.expectedType));
428            }
429            result.append("\nArgument types:\n");
430            if (inferenceErrorData.receiverArgumentType != null) {
431                result.append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".");
432            }
433            result.append("(").append(StringUtil.join(inferenceErrorData.valueArgumentsTypes, new Function<JetType, String>() {
434                @Override
435                public String fun(JetType type) {
436                    return RENDER_TYPE.render(type);
437                }
438            }, ", ")).append(")");
439            return result.toString();
440        }
441    
442        private Renderers() {
443        }
444    }