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        public static final Renderer<ClassDescriptor> RENDER_CLASS_OR_OBJECT_NAME = new Renderer<ClassDescriptor>() {
119            @NotNull
120            @Override
121            public String render(@NotNull ClassDescriptor classifier) {
122                return RenderingPackage.renderKindWithName(classifier);
123            }
124        };
125    
126        public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
127            @NotNull
128            @Override
129            public String render(@NotNull JetType type) {
130                return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type);
131            }
132        };
133    
134        public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
135                new Renderer<Collection<? extends ResolvedCall<?>>>() {
136                    @NotNull
137                    @Override
138                    public String render(@NotNull Collection<? extends ResolvedCall<?>> argument) {
139                        StringBuilder stringBuilder = new StringBuilder("\n");
140                        for (ResolvedCall<?> call : argument) {
141                            stringBuilder.append(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(call.getResultingDescriptor())).append("\n");
142                        }
143                        return stringBuilder.toString();
144                    }
145                };
146    
147        public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
148            return new Renderer<Collection<? extends T>>() {
149                @NotNull
150                @Override
151                public String render(@NotNull Collection<? extends T> object) {
152                    StringBuilder result = new StringBuilder();
153                    for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
154                        T next = iterator.next();
155                        result.append(itemRenderer.render(next));
156                        if (iterator.hasNext()) {
157                            result.append(", ");
158                        }
159                    }
160                    return result.toString();
161                }
162            };
163        }
164    
165        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
166                new Renderer<InferenceErrorData>() {
167                    @NotNull
168                    @Override
169                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
170                        return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
171                    }
172                };
173    
174        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
175                new Renderer<InferenceErrorData>() {
176                    @NotNull
177                    @Override
178                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
179                        return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
180                    }
181                };
182    
183        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
184                new Renderer<InferenceErrorData>() {
185                    @NotNull
186                    @Override
187                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
188                        return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
189                    }
190                };
191    
192        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
193                new Renderer<InferenceErrorData>() {
194                    @NotNull
195                    @Override
196                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
197                        return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
198                    }
199                };
200    
201        public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
202                TabledDescriptorRenderer result) {
203            LOG.assertTrue(inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints(), renderDebugMessage(
204                    "Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData));
205    
206            Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
207            Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
208                    inferenceErrorData.constraintSystem);
209            for (TypeSubstitutor substitutor : substitutors) {
210                CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
211                substitutedDescriptors.add(substitutedDescriptor);
212            }
213    
214            TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
215            if (firstConflictingParameter == null) {
216                LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData));
217                return result;
218            }
219    
220            result.text(newText()
221                                .normal("Cannot infer type parameter ")
222                                .strong(firstConflictingParameter.getName())
223                                .normal(" in "));
224            //String type = strong(firstConflictingParameter.getName());
225            TableRenderer table = newTable();
226            result.table(table);
227            table.descriptor(inferenceErrorData.descriptor)
228                 .text("None of the following substitutions");
229    
230            for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
231                JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
232    
233                final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
234                List<JetType> parameterTypes = Lists.newArrayList();
235                for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
236                    parameterTypes.add(valueParameterDescriptor.getType());
237                    if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
238                    JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
239                    if (!JetTypeChecker.DEFAULT.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
240                        errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
241                    }
242                }
243    
244                if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
245                        !JetTypeChecker.DEFAULT.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
246                    errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
247                }
248    
249                Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
250                    @Override
251                    public boolean apply(@Nullable ConstraintPosition constraintPosition) {
252                        return errorPositions.contains(constraintPosition);
253                    }
254                };
255                table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
256            }
257    
258            table.text("can be applied to")
259                    .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
260    
261            return result;
262        }
263    
264        public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
265                TabledDescriptorRenderer renderer) {
266            Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
267                @Override
268                public boolean apply(ConstraintPosition constraintPosition) {
269                    return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
270                }
271            };
272            return renderer.table(TabledDescriptorRenderer.newTable()
273                                          .descriptor(inferenceErrorData.descriptor)
274                                          .text("cannot be applied to")
275                                          .functionArgumentTypeList(
276                                                  inferenceErrorData.receiverArgumentType,
277                                                  inferenceErrorData.valueArgumentsTypes,
278                                                  isErrorPosition));
279        }
280    
281        public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
282                TabledDescriptorRenderer result) {
283            TypeParameterDescriptor firstUnknownParameter = null;
284            for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
285                if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
286                    firstUnknownParameter = typeParameter;
287                    break;
288                }
289            }
290            if (firstUnknownParameter == null) {
291                LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData));
292                return result;
293            }
294    
295            return result
296                    .text(newText().normal("Not enough information to infer parameter ")
297                                  .strong(firstUnknownParameter.getName())
298                                  .normal(" in "))
299                    .table(newTable()
300                                   .descriptor(inferenceErrorData.descriptor)
301                                   .text("Please specify it explicitly."));
302        }
303    
304        @NotNull
305        public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
306            TypeParameterDescriptor typeParameterDescriptor = null;
307            ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
308            ConstraintSystemStatus status = constraintSystem.getStatus();
309            LOG.assertTrue(status.hasViolatedUpperBound(), renderDebugMessage(
310                    "Upper bound violated renderer is applied for incorrect status", inferenceErrorData));
311    
312            ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
313            for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
314                if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
315                    typeParameterDescriptor = typeParameter;
316                }
317            }
318            if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
319                return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result);
320            }
321            if (typeParameterDescriptor == null) {
322                LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error",
323                                             inferenceErrorData));
324                return result;
325            }
326    
327            JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
328            if (inferredValueForTypeParameter == null) {
329                LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
330                                             typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData));
331                return result;
332            }
333    
334            result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
335                    .table(newTable().
336                            descriptor(inferenceErrorData.descriptor));
337    
338            JetType violatedUpperBound = null;
339            for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
340                JetType upperBoundWithSubstitutedInferredTypes =
341                        systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
342                if (upperBoundWithSubstitutedInferredTypes != null &&
343                    !JetTypeChecker.DEFAULT.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
344                    violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
345                    break;
346                }
347            }
348            if (violatedUpperBound == null) {
349                LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + typeParameterDescriptor.getName() +
350                                             " violates no bounds after substitution", inferenceErrorData));
351                return result;
352            }
353    
354            Renderer<JetType> typeRenderer = result.getTypeRenderer();
355            result.text(newText()
356                                .normal(" is not satisfied: inferred type ")
357                                .error(typeRenderer.render(inferredValueForTypeParameter))
358                                .normal(" is not a subtype of ")
359                                .strong(typeRenderer.render(violatedUpperBound)));
360            return result;
361        }
362    
363        public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
364            @NotNull
365            @Override
366            public String render(@NotNull Collection<ClassDescriptor> descriptors) {
367                StringBuilder sb = new StringBuilder();
368                int index = 0;
369                for (ClassDescriptor descriptor : descriptors) {
370                    sb.append(DescriptorUtils.getFqName(descriptor).asString());
371                    index++;
372                    if (index <= descriptors.size() - 2) {
373                        sb.append(", ");
374                    }
375                    else if (index == descriptors.size() - 1) {
376                        sb.append(" or ");
377                    }
378                }
379                return sb.toString();
380            }
381        };
382    
383        public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
384            @NotNull
385            @Override
386            public String render(@NotNull Collection<JetType> types) {
387                return StringUtil.join(types, new Function<JetType, String>() {
388                    @Override
389                    public String fun(JetType type) {
390                        return RENDER_TYPE.render(type);
391                    }
392                }, ", ");
393            }
394        };
395    
396        public static final Renderer<ConstraintSystem> RENDER_CONSTRAINT_SYSTEM = new Renderer<ConstraintSystem>() {
397            @NotNull
398            @Override
399            public String render(@NotNull ConstraintSystem constraintSystem) {
400                Set<TypeParameterDescriptor> typeVariables = constraintSystem.getTypeVariables();
401                Set<TypeBounds> typeBounds = Sets.newLinkedHashSet();
402                for (TypeParameterDescriptor variable : typeVariables) {
403                    typeBounds.add(constraintSystem.getTypeBounds(variable));
404                }
405                Function<TypeBounds, String> renderTypeBounds = rendererToFunction(RENDER_TYPE_BOUNDS);
406                return "type parameter bounds:\n" + StringUtil.join(typeBounds, renderTypeBounds, "\n") + "\n" +
407                       "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.getStatus());
408            }
409        };
410    
411        public static final Renderer<TypeBounds> RENDER_TYPE_BOUNDS = new Renderer<TypeBounds>() {
412            @NotNull
413            @Override
414            public String render(@NotNull TypeBounds typeBounds) {
415                Function<TypeBoundsImpl.Bound, String> renderBound = new Function<TypeBoundsImpl.Bound, String>() {
416                    @Override
417                    public String fun(TypeBoundsImpl.Bound bound) {
418                        String arrow = bound.kind == LOWER_BOUND ? ">: " : bound.kind == UPPER_BOUND ? "<: " : ":= ";
419                        return arrow + RENDER_TYPE.render(bound.type) + '(' + bound.position + ')';
420                    }
421                };
422                Name typeVariableName = typeBounds.getTypeVariable().getName();
423                if (typeBounds.isEmpty()) {
424                    return typeVariableName.asString();
425                }
426                return typeVariableName + " " + StringUtil.join(typeBounds.getBounds(), renderBound, ", ");
427            }
428        };
429    
430        @NotNull
431        public static <T> Function<T, String> rendererToFunction(final @NotNull Renderer<T> renderer) {
432            return new Function<T, String>() {
433                @Override
434                public String fun(T t) {
435                    return renderer.render(t);
436                }
437            };
438        }
439    
440        @NotNull
441        private static String renderDebugMessage(String message, InferenceErrorData inferenceErrorData) {
442            StringBuilder result = new StringBuilder();
443            result.append(message);
444            result.append("\nConstraint system: \n");
445            result.append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem));
446            result.append("\nDescriptor:\n");
447            result.append(inferenceErrorData.descriptor);
448            result.append("\nExpected type:\n");
449            if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
450                result.append(inferenceErrorData.expectedType);
451            }
452            else {
453                result.append(RENDER_TYPE.render(inferenceErrorData.expectedType));
454            }
455            result.append("\nArgument types:\n");
456            if (inferenceErrorData.receiverArgumentType != null) {
457                result.append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".");
458            }
459            result.append("(").append(StringUtil.join(inferenceErrorData.valueArgumentsTypes, new Function<JetType, String>() {
460                @Override
461                public String fun(JetType type) {
462                    return RENDER_TYPE.render(type);
463                }
464            }, ", ")).append(")");
465            return result.toString();
466        }
467    
468        private Renderers() {
469        }
470    }