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
017package org.jetbrains.jet.lang.diagnostics.rendering;
018
019import com.google.common.base.Predicate;
020import com.google.common.collect.Lists;
021import com.google.common.collect.Sets;
022import com.intellij.openapi.util.text.StringUtil;
023import com.intellij.psi.PsiElement;
024import com.intellij.util.Function;
025import org.jetbrains.annotations.NotNull;
026import org.jetbrains.annotations.Nullable;
027import org.jetbrains.jet.lang.descriptors.*;
028import org.jetbrains.jet.lang.psi.JetClass;
029import org.jetbrains.jet.lang.psi.JetClassOrObject;
030import org.jetbrains.jet.lang.resolve.DescriptorUtils;
031import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintPosition;
032import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintsUtil;
033import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
034import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
035import org.jetbrains.jet.lang.types.JetType;
036import org.jetbrains.jet.lang.types.TypeSubstitutor;
037import org.jetbrains.jet.lang.types.Variance;
038import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
039import org.jetbrains.jet.renderer.DescriptorRenderer;
040
041import java.util.Collection;
042import java.util.Iterator;
043import java.util.List;
044
045import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
046import static org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData.ExtendedInferenceErrorData;
047
048public 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}