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.resolve.DescriptorUtils;
032 import org.jetbrains.jet.lang.resolve.calls.inference.*;
033 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
034 import org.jetbrains.jet.lang.resolve.name.Name;
035 import org.jetbrains.jet.lang.types.JetType;
036 import org.jetbrains.jet.lang.types.TypeSubstitutor;
037 import org.jetbrains.jet.lang.types.TypeUtils;
038 import org.jetbrains.jet.lang.types.Variance;
039 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
040 import org.jetbrains.jet.renderer.DescriptorRenderer;
041 import org.jetbrains.jet.renderer.Renderer;
042
043 import java.util.Collection;
044 import java.util.Iterator;
045 import java.util.List;
046 import java.util.Set;
047
048 import static org.jetbrains.jet.lang.diagnostics.rendering.TabledDescriptorRenderer.*;
049 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND;
050 import static org.jetbrains.jet.lang.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND;
051
052 public class Renderers {
053 private static final Logger LOG = Logger.getInstance(Renderers.class);
054
055 public static final Renderer<Object> TO_STRING = new Renderer<Object>() {
056 @NotNull
057 @Override
058 public String render(@NotNull Object element) {
059 return element.toString();
060 }
061
062 @Override
063 public String toString() {
064 return "TO_STRING";
065 }
066 };
067
068 public static final Renderer<Object> NAME = new Renderer<Object>() {
069 @NotNull
070 @Override
071 public String render(@NotNull Object element) {
072 if (element instanceof Named) {
073 return ((Named) element).getName().asString();
074 }
075 return element.toString();
076 }
077 };
078
079 public static final Renderer<PsiElement> ELEMENT_TEXT = new Renderer<PsiElement>() {
080 @NotNull
081 @Override
082 public String render(@NotNull PsiElement element) {
083 return element.getText();
084 }
085 };
086
087 public static final Renderer<JetClassOrObject> RENDER_CLASS_OR_OBJECT = new Renderer<JetClassOrObject>() {
088 @NotNull
089 @Override
090 public String render(@NotNull JetClassOrObject classOrObject) {
091 String name = classOrObject.getName() != null ? " '" + classOrObject.getName() + "'" : "";
092 if (classOrObject instanceof JetClass) {
093 return "Class" + name;
094 }
095 return "Object" + name;
096
097 }
098 };
099
100 public static final Renderer<JetType> RENDER_TYPE = new Renderer<JetType>() {
101 @NotNull
102 @Override
103 public String render(@NotNull JetType type) {
104 return DescriptorRenderer.TEXT.renderType(type);
105 }
106 };
107
108 public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
109 new Renderer<Collection<? extends ResolvedCall<? extends CallableDescriptor>>>() {
110 @NotNull
111 @Override
112 public String render(@NotNull Collection<? extends ResolvedCall<? extends CallableDescriptor>> argument) {
113 StringBuilder stringBuilder = new StringBuilder("\n");
114 for (ResolvedCall<? extends CallableDescriptor> call : argument) {
115 stringBuilder.append(DescriptorRenderer.TEXT.render(call.getResultingDescriptor())).append("\n");
116 }
117 return stringBuilder.toString();
118 }
119 };
120
121 public static <T> Renderer<Collection<? extends T>> commaSeparated(final Renderer<T> itemRenderer) {
122 return new Renderer<Collection<? extends T>>() {
123 @NotNull
124 @Override
125 public String render(@NotNull Collection<? extends T> object) {
126 StringBuilder result = new StringBuilder();
127 for (Iterator<? extends T> iterator = object.iterator(); iterator.hasNext(); ) {
128 T next = iterator.next();
129 result.append(itemRenderer.render(next));
130 if (iterator.hasNext()) {
131 result.append(", ");
132 }
133 }
134 return result.toString();
135 }
136 };
137 }
138
139 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS_RENDERER =
140 new Renderer<InferenceErrorData>() {
141 @NotNull
142 @Override
143 public String render(@NotNull InferenceErrorData inferenceErrorData) {
144 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
145 }
146 };
147
148 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_TYPE_CONSTRUCTOR_MISMATCH_RENDERER =
149 new Renderer<InferenceErrorData>() {
150 @NotNull
151 @Override
152 public String render(@NotNull InferenceErrorData inferenceErrorData) {
153 return renderTypeConstructorMismatchError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
154 }
155 };
156
157 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER_RENDERER =
158 new Renderer<InferenceErrorData>() {
159 @NotNull
160 @Override
161 public String render(@NotNull InferenceErrorData inferenceErrorData) {
162 return renderNoInformationForParameterError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
163 }
164 };
165
166 public static final Renderer<InferenceErrorData> TYPE_INFERENCE_UPPER_BOUND_VIOLATED_RENDERER =
167 new Renderer<InferenceErrorData>() {
168 @NotNull
169 @Override
170 public String render(@NotNull InferenceErrorData inferenceErrorData) {
171 return renderUpperBoundViolatedInferenceError(inferenceErrorData, TabledDescriptorRenderer.create()).toString();
172 }
173 };
174
175 public static TabledDescriptorRenderer renderConflictingSubstitutionsInferenceError(InferenceErrorData inferenceErrorData,
176 TabledDescriptorRenderer result) {
177 LOG.assertTrue(inferenceErrorData.constraintSystem.getStatus().hasConflictingConstraints(), renderDebugMessage(
178 "Conflicting substitutions inference error renderer is applied for incorrect status", inferenceErrorData));
179
180 Collection<CallableDescriptor> substitutedDescriptors = Lists.newArrayList();
181 Collection<TypeSubstitutor> substitutors = ConstraintsUtil.getSubstitutorsForConflictingParameters(
182 inferenceErrorData.constraintSystem);
183 for (TypeSubstitutor substitutor : substitutors) {
184 CallableDescriptor substitutedDescriptor = inferenceErrorData.descriptor.substitute(substitutor);
185 substitutedDescriptors.add(substitutedDescriptor);
186 }
187
188 TypeParameterDescriptor firstConflictingParameter = ConstraintsUtil.getFirstConflictingParameter(inferenceErrorData.constraintSystem);
189 if (firstConflictingParameter == null) {
190 LOG.error(renderDebugMessage("There is no conflicting parameter for 'conflicting constraints' error.", inferenceErrorData));
191 return result;
192 }
193
194 result.text(newText()
195 .normal("Cannot infer type parameter ")
196 .strong(firstConflictingParameter.getName())
197 .normal(" in "));
198 //String type = strong(firstConflictingParameter.getName());
199 TableRenderer table = newTable();
200 result.table(table);
201 table.descriptor(inferenceErrorData.descriptor)
202 .text("None of the following substitutions");
203
204 for (CallableDescriptor substitutedDescriptor : substitutedDescriptors) {
205 JetType receiverType = DescriptorUtils.getReceiverParameterType(substitutedDescriptor.getReceiverParameter());
206
207 final Collection<ConstraintPosition> errorPositions = Sets.newHashSet();
208 List<JetType> parameterTypes = Lists.newArrayList();
209 for (ValueParameterDescriptor valueParameterDescriptor : substitutedDescriptor.getValueParameters()) {
210 parameterTypes.add(valueParameterDescriptor.getType());
211 if (valueParameterDescriptor.getIndex() >= inferenceErrorData.valueArgumentsTypes.size()) continue;
212 JetType actualType = inferenceErrorData.valueArgumentsTypes.get(valueParameterDescriptor.getIndex());
213 if (!JetTypeChecker.INSTANCE.isSubtypeOf(actualType, valueParameterDescriptor.getType())) {
214 errorPositions.add(ConstraintPosition.getValueParameterPosition(valueParameterDescriptor.getIndex()));
215 }
216 }
217
218 if (receiverType != null && inferenceErrorData.receiverArgumentType != null &&
219 !JetTypeChecker.INSTANCE.isSubtypeOf(inferenceErrorData.receiverArgumentType, receiverType)) {
220 errorPositions.add(ConstraintPosition.RECEIVER_POSITION);
221 }
222
223 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
224 @Override
225 public boolean apply(@Nullable ConstraintPosition constraintPosition) {
226 return errorPositions.contains(constraintPosition);
227 }
228 };
229 table.functionArgumentTypeList(receiverType, parameterTypes, isErrorPosition);
230 }
231
232 table.text("can be applied to")
233 .functionArgumentTypeList(inferenceErrorData.receiverArgumentType, inferenceErrorData.valueArgumentsTypes);
234
235 return result;
236 }
237
238 public static TabledDescriptorRenderer renderTypeConstructorMismatchError(final InferenceErrorData inferenceErrorData,
239 TabledDescriptorRenderer renderer) {
240 Predicate<ConstraintPosition> isErrorPosition = new Predicate<ConstraintPosition>() {
241 @Override
242 public boolean apply(ConstraintPosition constraintPosition) {
243 return inferenceErrorData.constraintSystem.getStatus().hasTypeConstructorMismatchAt(constraintPosition);
244 }
245 };
246 return renderer.table(TabledDescriptorRenderer.newTable()
247 .descriptor(inferenceErrorData.descriptor)
248 .text("cannot be applied to")
249 .functionArgumentTypeList(
250 inferenceErrorData.receiverArgumentType,
251 inferenceErrorData.valueArgumentsTypes,
252 isErrorPosition));
253 }
254
255 public static TabledDescriptorRenderer renderNoInformationForParameterError(InferenceErrorData inferenceErrorData,
256 TabledDescriptorRenderer result) {
257 TypeParameterDescriptor firstUnknownParameter = null;
258 for (TypeParameterDescriptor typeParameter : inferenceErrorData.constraintSystem.getTypeVariables()) {
259 if (inferenceErrorData.constraintSystem.getTypeBounds(typeParameter).isEmpty()) {
260 firstUnknownParameter = typeParameter;
261 break;
262 }
263 }
264 if (firstUnknownParameter == null) {
265 LOG.error(renderDebugMessage("There is no unknown parameter for 'no information for parameter error'.", inferenceErrorData));
266 return result;
267 }
268
269 return result
270 .text(newText().normal("Not enough information to infer parameter ")
271 .strong(firstUnknownParameter.getName())
272 .normal(" in "))
273 .table(newTable()
274 .descriptor(inferenceErrorData.descriptor)
275 .text("Please specify it explicitly."));
276 }
277
278 @NotNull
279 public static TabledDescriptorRenderer renderUpperBoundViolatedInferenceError(InferenceErrorData inferenceErrorData, TabledDescriptorRenderer result) {
280 TypeParameterDescriptor typeParameterDescriptor = null;
281 ConstraintSystemImpl constraintSystem = (ConstraintSystemImpl) inferenceErrorData.constraintSystem;
282 ConstraintSystemStatus status = constraintSystem.getStatus();
283 LOG.assertTrue(status.hasViolatedUpperBound(), renderDebugMessage(
284 "Upper bound violated renderer is applied for incorrect status", inferenceErrorData));
285
286 ConstraintSystem systemWithoutWeakConstraints = constraintSystem.getSystemWithoutWeakConstraints();
287 for (TypeParameterDescriptor typeParameter : inferenceErrorData.descriptor.getTypeParameters()) {
288 if (!ConstraintsUtil.checkUpperBoundIsSatisfied(systemWithoutWeakConstraints, typeParameter, true)) {
289 typeParameterDescriptor = typeParameter;
290 }
291 }
292 if (typeParameterDescriptor == null && status.hasConflictingConstraints()) {
293 return renderConflictingSubstitutionsInferenceError(inferenceErrorData, result);
294 }
295 if (typeParameterDescriptor == null) {
296 LOG.error(renderDebugMessage("There is no type parameter with violated upper bound for 'upper bound violated' error",
297 inferenceErrorData));
298 return result;
299 }
300
301 JetType inferredValueForTypeParameter = systemWithoutWeakConstraints.getTypeBounds(typeParameterDescriptor).getValue();
302 if (inferredValueForTypeParameter == null) {
303 LOG.error(renderDebugMessage("System without weak constraints is not successful, there is no value for type parameter " +
304 typeParameterDescriptor.getName() + "\n: " + systemWithoutWeakConstraints, inferenceErrorData));
305 return result;
306 }
307
308 result.text(newText().normal("Type parameter bound for ").strong(typeParameterDescriptor.getName()).normal(" in "))
309 .table(newTable().
310 descriptor(inferenceErrorData.descriptor));
311
312 JetType violatedUpperBound = null;
313 for (JetType upperBound : typeParameterDescriptor.getUpperBounds()) {
314 JetType upperBoundWithSubstitutedInferredTypes =
315 systemWithoutWeakConstraints.getResultingSubstitutor().substitute(upperBound, Variance.INVARIANT);
316 if (upperBoundWithSubstitutedInferredTypes != null &&
317 !JetTypeChecker.INSTANCE.isSubtypeOf(inferredValueForTypeParameter, upperBoundWithSubstitutedInferredTypes)) {
318 violatedUpperBound = upperBoundWithSubstitutedInferredTypes;
319 break;
320 }
321 }
322 if (violatedUpperBound == null) {
323 LOG.error(renderDebugMessage("Type parameter (chosen as violating its upper bound)" + typeParameterDescriptor.getName() +
324 " violates no bounds after substitution", inferenceErrorData));
325 return result;
326 }
327
328 Renderer<JetType> typeRenderer = result.getTypeRenderer();
329 result.text(newText()
330 .normal(" is not satisfied: inferred type ")
331 .error(typeRenderer.render(inferredValueForTypeParameter))
332 .normal(" is not a subtype of ")
333 .strong(typeRenderer.render(violatedUpperBound)));
334 return result;
335 }
336
337 public static final Renderer<Collection<ClassDescriptor>> CLASSES_OR_SEPARATED = new Renderer<Collection<ClassDescriptor>>() {
338 @NotNull
339 @Override
340 public String render(@NotNull Collection<ClassDescriptor> descriptors) {
341 StringBuilder sb = new StringBuilder();
342 int index = 0;
343 for (ClassDescriptor descriptor : descriptors) {
344 sb.append(DescriptorUtils.getFqName(descriptor).asString());
345 index++;
346 if (index <= descriptors.size() - 2) {
347 sb.append(", ");
348 }
349 else if (index == descriptors.size() - 1) {
350 sb.append(" or ");
351 }
352 }
353 return sb.toString();
354 }
355 };
356
357 public static final Renderer<Collection<JetType>> RENDER_COLLECTION_OF_TYPES = new Renderer<Collection<JetType>>() {
358 @NotNull
359 @Override
360 public String render(@NotNull Collection<JetType> types) {
361 return StringUtil.join(types, new Function<JetType, String>() {
362 @Override
363 public String fun(JetType type) {
364 return RENDER_TYPE.render(type);
365 }
366 }, ", ");
367 }
368 };
369
370 public static final Renderer<ConstraintSystem> RENDER_CONSTRAINT_SYSTEM = new Renderer<ConstraintSystem>() {
371 @NotNull
372 @Override
373 public String render(@NotNull ConstraintSystem constraintSystem) {
374 Set<TypeParameterDescriptor> typeVariables = constraintSystem.getTypeVariables();
375 Set<TypeBounds> typeBounds = Sets.newLinkedHashSet();
376 for (TypeParameterDescriptor variable : typeVariables) {
377 typeBounds.add(constraintSystem.getTypeBounds(variable));
378 }
379 Function<TypeBounds, String> renderTypeBounds = rendererToFunction(RENDER_TYPE_BOUNDS);
380 return "type parameter bounds:\n" + StringUtil.join(typeBounds, renderTypeBounds, "\n") + "\n" +
381 "status:\n" + ConstraintsUtil.getDebugMessageForStatus(constraintSystem.getStatus());
382 }
383 };
384
385 public static final Renderer<TypeBounds> RENDER_TYPE_BOUNDS = new Renderer<TypeBounds>() {
386 @NotNull
387 @Override
388 public String render(@NotNull TypeBounds typeBounds) {
389 Function<TypeBoundsImpl.Bound, String> renderBound = new Function<TypeBoundsImpl.Bound, String>() {
390 @Override
391 public String fun(TypeBoundsImpl.Bound bound) {
392 String arrow = bound.kind == LOWER_BOUND ? ">: " : bound.kind == UPPER_BOUND ? "<: " : ":= ";
393 return arrow + RENDER_TYPE.render(bound.type) + '(' + bound.position + ')';
394 }
395 };
396 Name typeVariableName = typeBounds.getTypeVariable().getName();
397 if (typeBounds.isEmpty()) {
398 return typeVariableName.asString();
399 }
400 return typeVariableName + " " + StringUtil.join(typeBounds.getBounds(), renderBound, ", ");
401 }
402 };
403
404 @NotNull
405 public static <T> Function<T, String> rendererToFunction(final @NotNull Renderer<T> renderer) {
406 return new Function<T, String>() {
407 @Override
408 public String fun(T t) {
409 return renderer.render(t);
410 }
411 };
412 }
413
414 @NotNull
415 private static String renderDebugMessage(String message, InferenceErrorData inferenceErrorData) {
416 StringBuilder result = new StringBuilder();
417 result.append(message);
418 result.append("\nConstraint system: \n");
419 result.append(RENDER_CONSTRAINT_SYSTEM.render(inferenceErrorData.constraintSystem));
420 result.append("\nDescriptor:\n");
421 result.append(inferenceErrorData.descriptor);
422 result.append("\nExpected type:\n");
423 if (TypeUtils.noExpectedType(inferenceErrorData.expectedType)) {
424 result.append(inferenceErrorData.expectedType);
425 }
426 else {
427 result.append(RENDER_TYPE.render(inferenceErrorData.expectedType));
428 }
429 result.append("\nArgument types:\n");
430 if (inferenceErrorData.receiverArgumentType != null) {
431 result.append(RENDER_TYPE.render(inferenceErrorData.receiverArgumentType)).append(".");
432 }
433 result.append("(").append(StringUtil.join(inferenceErrorData.valueArgumentsTypes, new Function<JetType, String>() {
434 @Override
435 public String fun(JetType type) {
436 return RENDER_TYPE.render(type);
437 }
438 }, ", ")).append(")");
439 return result.toString();
440 }
441
442 private Renderers() {
443 }
444 }