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 }