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