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