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.util.text.StringUtil;
023    import com.intellij.psi.PsiElement;
024    import com.intellij.util.Function;
025    import org.jetbrains.annotations.NotNull;
026    import org.jetbrains.annotations.Nullable;
027    import org.jetbrains.jet.lang.descriptors.*;
028    import org.jetbrains.jet.lang.psi.JetClass;
029    import org.jetbrains.jet.lang.psi.JetClassOrObject;
030    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
032    import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
033    import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
034    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
035    import org.jetbrains.jet.lang.types.JetType;
036    import org.jetbrains.jet.lang.types.TypeSubstitutor;
037    import org.jetbrains.jet.lang.types.Variance;
038    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
039    import org.jetbrains.jet.renderer.DescriptorRenderer;
040    
041    import java.util.Collection;
042    import java.util.Iterator;
043    import java.util.List;
044    
045    import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
046    import static org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData.ExtendedInferenceErrorData;
047    
048    public class Renderers {
049        public static final Renderer<Object> TO_STRING = new Renderer<Object>() {
050            @NotNull
051            @Override
052            public String render(@NotNull Object element) {
053                return element.toString();
054            }
055    
056            @Override
057            public String toString() {
058                return "TO_STRING";
059            }
060        };
061    
062        public static final Renderer<Object> NAME = new Renderer<Object>() {
063            @NotNull
064            @Override
065            public String render(@NotNull Object element) {
066                if (element instanceof Named) {
067                    return ((Named) element).getName().asString();
068                }
069                return element.toString();
070            }
071        };
072    
073        public static final Renderer<PsiElement> ELEMENT_TEXT = new Renderer<PsiElement>() {
074            @NotNull
075            @Override
076            public String render(@NotNull PsiElement element) {
077                return element.getText();
078            }
079        };
080        
081        public static final Renderer<JetClassOrObject> RENDER_CLASS_OR_OBJECT = new Renderer<JetClassOrObject>() {
082            @NotNull
083            @Override
084            public String render(@NotNull JetClassOrObject classOrObject) {
085                String name = classOrObject.getName() != null ? " '" + classOrObject.getName() + "'" : "";
086                if (classOrObject instanceof JetClass) {
087                    return "Class" + name;
088                }
089                return "Object" + name;
090    
091            }
092        };
093    
094        public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
095            @NotNull
096            @Override
097            public String render(@NotNull JetType type) {
098                return DescriptorRenderer.TEXT.renderType(type);
099            }
100        };
101    
102        public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
103                new Renderer<Collection<? extends ResolvedCall<? extends CallableDescriptor>>>() {
104                    @NotNull
105                    @Override
106                    public String render(@NotNull Collection<? extends ResolvedCall<? extends CallableDescriptor>> argument) {
107                        StringBuilder stringBuilder = new StringBuilder("\n");
108                        for (ResolvedCall<? extends CallableDescriptor> call : argument) {
109                            stringBuilder.append(DescriptorRenderer.TEXT.render(call.getResultingDescriptor())).append("\n");
110                        }
111                        return stringBuilder.toString();
112                    }
113                };
114    
115        public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
116            return new Renderer<Collection<? extends T>>() {
117                @NotNull
118                @Override
119                public String render(@NotNull Collection<? extends T> object) {
120                    StringBuilder result = new StringBuilder();
121                    for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
122                        T next = iterator.next();
123                        result.append(itemRenderer.render(next));
124                        if (iterator.hasNext()) {
125                            result.append(", ");
126                        }
127                    }
128                    return result.toString();
129                }
130            };
131        }
132    
133        public static final Renderer<ExtendedInferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
134                new Renderer<ExtendedInferenceErrorData>() {
135                    @NotNull
136                    @Override
137                    public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) {
138                        return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
139                    }
140                };
141    
142        public static final Renderer<ExtendedInferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
143                new Renderer<ExtendedInferenceErrorData>() {
144                    @NotNull
145                    @Override
146                    public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) {
147                        return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
148                    }
149                };
150    
151        public static final Renderer<ExtendedInferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
152                new Renderer<ExtendedInferenceErrorData>() {
153                    @NotNull
154                    @Override
155                    public String render(@NotNull ExtendedInferenceErrorData inferenceErrorData) {
156                        return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
157                    }
158                };
159    
160        public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
161                new Renderer<InferenceErrorData>() {
162                    @NotNull
163                    @Override
164                    public String render(@NotNull InferenceErrorData inferenceErrorData) {
165                        return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
166                    }
167                };
168    
169        public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(ExtendedInferenceErrorData inferenceErrorData,
170                TabledDescriptorRenderer result) {
171            assert inferenceErrorData.constraintSystem.hasConflictingConstraints();
172    
173            Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
174            Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
175                    inferenceErrorData.constraintSystem);
176            for (TypeSubstitutor substitutor : substitutors) {
177                CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
178                substitutedDescriptors.add(substitutedDescriptor);
179            }
180    
181            TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
182            assert firstConflictingParameter != null;
183    
184            result.text(newText()
185                                .normal("Cannot infer type parameter ")
186                                .strong(firstConflictingParameter.getName())
187                                .normal(" in "));
188            //String type = strong(firstConflictingParameter.getName());
189            TableRenderer table = newTable();
190            result.table(table);
191            table.descriptor(inferenceErrorData.descriptor)
192                 .text("None of the following substitutions");
193    
194            for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
195                JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
196    
197                final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
198                List<JetType> parameterTypes = Lists.newArrayList();
199                for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
200                    parameterTypes.add(valueParameterDescriptor.getType());
201                    if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
202                    JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
203                    if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
204                        errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
205                    }
206                }
207    
208                if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
209                        !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
210                    errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
211                }
212    
213                Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
214                    @Override
215                    public boolean apply(@Nullable ConstraintPosition constraintPosition) {
216                        return errorPositions.contains(constraintPosition);
217                    }
218                };
219                table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
220            }
221    
222            table.text("can be applied to")
223                    .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
224    
225            return result;
226        }
227    
228        public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final ExtendedInferenceErrorData inferenceErrorData,
229                TabledDescriptorRenderer renderer) {
230            Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
231                @Override
232                public boolean apply(@Nullable ConstraintPosition constraintPosition) {
233                    assert constraintPosition != null;
234                    return inferenceErrorData.constraintSystem.hasTypeConstructorMismatchAt(constraintPosition);
235                }
236            };
237            return renderer.table(TabledDescriptorRenderer.newTable()
238                                          .descriptor(inferenceErrorData.descriptor)
239                                          .text("cannot be applied to")
240                                          .functionArgumentTypeList(
241                                                  inferenceErrorData.receiverArgumentType,
242                                                  inferenceErrorData.valueArgumentsTypes,
243                                                  isErrorPosition));
244        }
245    
246        public static TabledDescriptorRenderer renderNoInformationForParameterError(ExtendedInferenceErrorData inferenceErrorData,
247                TabledDescriptorRenderer renderer) {
248            TypeParameterDescriptor firstUnknownParameter = null;
249            for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
250                if (inferenceErrorData.constraintSystem.getTypeConstraints(typeParameter).isEmpty()) {
251                    firstUnknownParameter = typeParameter;
252                    break;
253                }
254            }
255            assert firstUnknownParameter != null;
256    
257            return renderer
258                    .text(newText().normal("Not enough information to infer parameter ")
259                                  .strong(firstUnknownParameter.getName())
260                                  .normal(" in "))
261                    .table(newTable()
262                                   .descriptor(inferenceErrorData.descriptor)
263                                   .text("Please specify it explicitly."));
264        }
265    
266        public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
267            TypeParameterDescriptor typeParameterDescriptor = null;
268            for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
269                if (!ConstraintsUtil.checkUpperBoundIsSatisfied(inferenceErrorData.constraintSystem, typeParameter, true)) {
270                    typeParameterDescriptor = typeParameter;
271                    break;
272                }
273            }
274            assert typeParameterDescriptor != null;
275    
276            result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
277                    .table(newTable().
278                            descriptor(inferenceErrorData.descriptor));
279    
280            JetType inferredValueForTypeParameter = ConstraintsUtil.getValue(inferenceErrorData.constraintSystem.getTypeConstraints(typeParameterDescriptor));
281            assert inferredValueForTypeParameter != null;
282            JetType upperBound = typeParameterDescriptor.getUpperBoundsAsType();
283            JetType upperBoundWithSubstitutedInferredTypes = inferenceErrorData.constraintSystem.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
284            assert upperBoundWithSubstitutedInferredTypes != null;
285    
286            Renderer<JetType> typeRenderer = result.getTypeRenderer();
287            result.text(newText()
288                                .normal(" is not satisfied: inferred type ")
289                                .error(typeRenderer.render(inferredValueForTypeParameter))
290                                .normal(" is not a subtype of ")
291                                .strong(typeRenderer.render(upperBoundWithSubstitutedInferredTypes)));
292            return result;
293        }
294    
295        public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
296            @NotNull
297            @Override
298            public String render(@NotNull Collection<ClassDescriptor> descriptors) {
299                StringBuilder sb = new StringBuilder();
300                int index = 0;
301                for (ClassDescriptor descriptor : descriptors) {
302                    sb.append(DescriptorUtils.getFQName(descriptor).asString());
303                    index++;
304                    if (index <= descriptors.size() - 2) {
305                        sb.append(", ");
306                    }
307                    else if (index == descriptors.size() - 1) {
308                        sb.append(" or ");
309                    }
310                }
311                return sb.toString();
312            }
313        };
314    
315        public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
316            @NotNull
317            @Override
318            public String render(@NotNull Collection<JetType> types) {
319                return StringUtil.join(types, new Function<JetType, String>() {
320                    @Override
321                    public String fun(JetType type) {
322                        return RENDER_TYPE.render(type);
323                    }
324                }, ", ");
325            }
326        };
327    
328    
329        private Renderers() {
330        }
331    }